关于java:对-JDK8-新出的Optional类的探索与思考

48次阅读

共计 7170 个字符,预计需要花费 18 分钟才能阅读完成。

by emanjusaka from https://www.emanjusaka.top/archives/1692754288680 彼岸花开可奈何
本文欢送分享与聚合,全文转载请留下原文地址。

引言

所有的 Java 程序员根本都会遇到 NullPointerException 异样,个别解决这个问题可能不会是很难,然而有时去排查到底是哪引起的会消耗很长时间很是麻烦。最近理解到 JDK1.8 新减少了一个 Optional 类能够防止一些 NullPointerException 异样,上面让咱们一起去理解一下它吧。

基于值的类(Value-based Classes)

有些类,如 java.util.Optional 和 java.time.LocalDateTime,是基于值的。基于值的类的实例:

  • 是最终的和不可变的(只管可能蕴含对可变对象的援用);
  • 具备 equals、hashCode 和 toString 的实现,这些实现仅依据实例的状态计算,而不是依据实例的标识或任何其余对象或变量的状态计算;
  • 不应用对身份敏感的操作,例如实例之间的援用相等(==)、实例的身份哈希代码或对实例的外部锁进行同步;
  • 仅基于 equals() 而不是基于援用相等 (==) 被视为相等;
  • 没有可拜访的构造函数,而是通过工厂办法进行实例化,这些办法不提交返回实例的标识;
  • 当相等时是可自在替换的,这意味着在任何计算或办法调用中,依据 equals() 替换任何两个相等的实例 x 和 y 都不会产生显著的行为变动。

如果程序试图辨别对基于值的类的相等值的两个援用,无论是间接通过援用相等,还是间接通过调用同步、身份哈希、序列化或任何其余身份敏感机制,都可能产生不可预测的后果。在基于值的类的实例上应用这种对身份敏感的操作可能会产生不可预测的影响,应该防止。

简略地说,基于值的类的实例是最终的,不可变的,并且这些实例没有适当的状态和标识,因而某些操作是特定于标识的,因而不应应用。

一、Optional 中的根本办法

Optional 类位于 java.util 包下,它是一个容器对象,可能蕴含也可能不蕴含非空值。

这是一个基于值的类; 在 Optional 实例上应用身份敏感操作 (包含援用相等(==)、身份哈希码或同步) 可能会产生不可预测的后果,应该防止。

1、创立办法

  • ​empty()​​

    返回一个空的 Optional 实例

    public class Main {public static void main(String[] args) {System.out.println(Optional);
        }
    }
    // 输入
    Optional.empty

    留神:不要通过与 Option.empty()​​返回的实例进行 ==​​比拟来防止测试对象是否为空, 因为不能保障它是单例的。

<!—->

  • ​of(T value)​​

    返回一个带值的 Optional,如果 value 是 null 会抛出 NullPointerException 异样

    public static void main(String[] args) {Optional<String> emanjusaka = Optional.of("emanjusaka");
            System.out.println(emanjusaka);
        }
    // 输入
    Optional[emanjusaka]
  • ​ofNullable(T value)​

    如果非空,返回形容指定值的 Optional,否则返回空 Optional。

    public static void main(String[] args) {Optional<String> emanjusaka = Optional.ofNullable("emanjusaka");
            System.out.println(emanjusaka);
    
            Optional<String> empty = Optional.ofNullable(null);
            System.out.println(empty);
        }
    // 输入
    Optional[emanjusaka]
    Optional.empty

2、判断办法

  • ​isPresent()​​

    如果存在值则返回 true,否则返回 false。

    public static void main(String[] args) {Optional<String> emanjusaka = Optional.ofNullable("emanjusaka");
            System.out.println(emanjusaka);
            System.out.println(emanjusaka.isPresent());
    
            Optional<String> empty = Optional.ofNullable(null);
            System.out.println(empty);
            System.out.println(empty.isPresent());
        }
    // 输入
    Optional[emanjusaka]
    true
    Optional.empty
    false
  • ​ifPresent(Consumer<? super T> consumer)​​

    如果存在值,则应用该值调用指定的消费者,否则什么都不做。

    public static void main(String[] args) {Optional<String> emanjusaka = Optional.of("emanjusaka");
            Consumer<String> consumer = s -> {
                s = "hello" + s;
                System.out.println(s);
            };
            System.out.println(emanjusaka);
            emanjusaka.ifPresent(consumer);
        }
    // 输入
    Optional[emanjusaka]
    hello emanjusaka

    个别用于判断 Optional 是否为空,并在不为空的状况下执行相应的操作。

3、获取办法

  • ​get()​​

    如果这个 Optional 中存在一个值,则返回该值,否则抛出 NoSuchElementException。

    public static void main(String[] args) {Optional<String> emanjusaka = Optional.ofNullable("emanjusaka");
            System.out.println(emanjusaka);
            System.out.println(emanjusaka.get());
    
            Optional<String> empty = Optional.ofNullable(null);
            System.out.println(empty);
            System.out.println(empty.get());
        }
    // 输入
    Optional[emanjusaka]
    emanjusaka
    Optional.empty
    Exception in thread "main" java.util.NoSuchElementException: No value present
        at java.util.Optional.get(Optional.java:135)
        at org.example.Main.main(Main.java:13)
  • ​filter(Predicate<? super T> predicate)​​

    如果存在一个值,并且该值与给定的过滤条件匹配,则返回一个形容该值的 Optional,否则返回一个空的 Optional。

     public static void main(String[] args) {Optional<String> emanjusaka = Optional.of("emanjusaka");
            System.out.println(emanjusaka);
            System.out.println(emanjusaka.filter(s -> {return "emanjusaka".equals(s);
            }));
            System.out.println(emanjusaka.filter(s -> false));
        }
    // 输入
    Optional[emanjusaka]
    Optional[emanjusaka]
    Optional.empty
  • ​map(Function<? super T, ? extends U> mapper)​​

    如果存在值,则将提供的映射函数利用于该值,如果后果为非 null,则返回一个形容后果的 Optional。否则返回空的 Optional。

    public static void main(String[] args) {User user = new User();
            user.setName("emanjusaka");
            Optional<User> optional = Optional.of(user);
            System.out.println(optional);
            System.out.println(optional.map(User::getName));
    
            User userEmpty = new User();
            Optional<User> optionalEmpty = Optional.of(userEmpty);
            System.out.println(optionalEmpty);
            System.out.println(optionalEmpty.map(User::getName));
        }
    
        private static class User {
            private String name;
    
            public String getName() {return name;}
    
            public void setName(String name) {this.name = name;}
        }
    // 输入
    Optional[org.example.Main$User@2503dbd3]
    Optional[emanjusaka]
    Optional[org.example.Main$User@6d03e736]
    Optional.empty
  • ​flatMap(Function<? super T, Optionalu> mapper)​​

    如果存在值,请将提供的 Optional 方位映射函数利用于该值,返回该后果,否则返回空的 Optional。此办法相似于 map(Function),但提供的 mapper 的后果曾经是 Optional,并且如果调用,flatMap 不会用额定的 Optional 包装它。

    public static void main(String[] args) {User user = new User();
            user.setName("emanjusaka");
            user.setAge(Optional.of(35));
            Optional<User> optional = Optional.of(user);
            System.out.println(optional);
            System.out.println(optional.map(User::getAge));
            System.out.println(optional.flatMap(User::getAge));
        }
    
        private static class User {
            private String name;
    
            private Optional<Integer> age;
    
            public String getName() {return name;}
    
            public void setName(String name) {this.name = name;}
    
            public Optional<Integer> getAge() {return age;}
    
            public void setAge(Optional<Integer> age) {this.age = age;}
        }
    // 输入
    Optional[org.example.Main$User@2503dbd3]
    Optional[Optional[35]]
    Optional[35]

    这个例子中体现出了 map 和 flatMap 办法的区别,map 办法会在返回时用 Optional 进行包装而 flatMap 办法不会再进行额定的包装。

  • ​orElse(T other)​​

    如果存在这个值则返回这个值,否则返回传入的值 other

    public static void main(String[] args) {Optional<String> optional = Optional.of("emanjusaka");
            System.out.println(optional);
            System.out.println(optional.orElse(null));
    
            Optional<String> optionalEmpty = Optional.ofNullable(null);
            System.out.println(optionalEmpty);
            System.out.println(optionalEmpty.orElse("empty"));
        }
    // 输入
    Optional[emanjusaka]
    emanjusaka
    Optional.empty
    empty
  • ​orElseGet(Supplier<? extends T> other)​​

    如果存在这个值则返回这个值,否则调用 other 并返回该调用的后果。

    public static void main(String[] args) {Optional<String> optional = Optional.of("emanjusaka");
            System.out.println(optional);
            System.out.println(optional.orElseGet(() -> "hello emanjusaka"));
    
            Optional<String> optionalEmpty = Optional.ofNullable(null);
            System.out.println(optionalEmpty);
            System.out.println(optionalEmpty.orElseGet(() -> "hello emanjusaka"));
        }
    // 输入
    Optional[emanjusaka]
    emanjusaka
    Optional.empty
    hello emanjusaka
  • ​orElseThrow(Supplier? extends X exceptionSupplier)​​

    返回蕴含的值(如果存在),否则抛出由所提供创立的异样。

    public static void main(String[] args) {Optional<String> optionalEmpty = Optional.ofNullable(null);
            System.out.println(optionalEmpty);
            System.out.println(optionalEmpty.orElseThrow(ArithmeticException::new));
        }
    // 输入
    Optional.empty
    Exception in thread "main" java.lang.ArithmeticException
        at java.util.Optional.orElseThrow(Optional.java:290)
        at org.example.Main.main(Main.java:9)

二、Optional 中办法的区别

1、map 和 flatMap 办法的区别

  • map 办法会在返回时用 Optional 进行包装而 flatMap 办法不会再进行额定的包装。

2、orElse 和 orElseGet 办法的区别

  • orElse():如果有值则将其返回,否则返回指定的 other。
  • orElseGet():如果有值则将其返回,否则调用函数 other 并将其返回调用后果。
  • orElse() 办法在 Optional 值为非空时,也会计算传入的参数,而 orElseGet() 办法只有在 Optional 值为空时才会执行传入的函数。

如果是传值能够选用 orElse()​​,如果传入的是办法选用 orElseGet()​​。

三、总结

在应用 Optional 时我感觉应该尽量避免一些状况:

  • 永远不要通过返回 Optional 的办法返回一个空值: 它毁坏 Optional 设计的初衷。
  • 并不是所有的返回类型都能从 Optional 的解决中获益。容器类型,包含汇合、映射、Stream、数组和 Optional,不应该封装在 Optional 中。与其返回一个空的 Optional<List<T>>,不还如返回一个 空的 List<T>。
  • 除了「主要根本类型(minor primitive types)」Boolean,Byte,Character,Short 和 Float 之外,永远不应该返回装箱的根本类型 的 Optional。

总之,如果发现自己编写的办法不能总是返回值,并且认为该办法的用户在每次调用时思考这种可能性很重要,那么或者应该返回一个 Optional 的办法。然而,应该意识到,返回 Optional 会带来理论的性能结果; 对于性能要害的办法,最好返回 null 或抛出异样。最初,除了作为返回值之外,不应该在任何其余中央中应用 Optional。

应用 Optional 时要留神,我认为它并不能完全避免空指针。如果这个值是 null,不做额定的判断,间接应用还是会有空指针的问题。应用 Optional 的益处是它能够帮忙咱们简化判空的操作,简洁咱们的代码。用了 Optional 最初拿后果的时候还是要小心的,自觉 get 一样会抛错。

四、参考文献

  1. 《Effective Java》
  2. jdk11 的文档

本文原创,满腹经纶,如有纰漏,欢送斧正。尊贵的敌人,如果本文对您有所帮忙,欢送点赞,并期待您的反馈,以便于一直优化。

原文地址:https://www.emanjusaka.top/archives/1692754288680

正文完
 0