JDK8新出Optional类的方法探索与思考分析

 更新时间:2023年08月23日 14:18:54   作者:彼岸花开可奈何  
这篇文章主要为大家介绍了JDK8新出Optional类的发方法示例探索与思考分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

所有的 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 一样会抛错。

参考文献

《Effective Java》jdk11的文档

以上就是JDK8新出Optional类的方法探索与思考分析的详细内容,更多关于JDK8 Optional类的资料请关注脚本之家其它相关文章!

相关文章

  • Java加载ICC文件的方法和示例代码

    Java加载ICC文件的方法和示例代码

    ICC文件,通常用于颜色管理,定义了如何将一个颜色空间转换为另一个颜色空间,在Java中,我们可能需要加载这些文件来进行颜色转换或管理,本文将为您提供加载ICC文件的方法和示例代码,需要的朋友参考下吧
    2023-08-08
  • java字符串与格式化输出的深入分析

    java字符串与格式化输出的深入分析

    本篇文章是对java字符串与格式化输出进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • idea手动执行maven命令的三种实现方式

    idea手动执行maven命令的三种实现方式

    这篇文章主要介绍了idea手动执行maven命令的三种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • Java网络通信中URL与HTTP编程技术详解

    Java网络通信中URL与HTTP编程技术详解

    要想实现网络编程,除了可以使用Socket之外,我们还可以利用URL编程或HTTP编程技术,所以今天这篇文章,就给大家介绍一下URL编程和HTTP编程技术,看看这两种技术有什么特点,文中有详细的代码讲解,需要的朋友可以参考下
    2023-11-11
  • 一个MIDP俄罗斯方块游戏的设计和实现

    一个MIDP俄罗斯方块游戏的设计和实现

    一个MIDP俄罗斯方块游戏的设计和实现...
    2006-12-12
  • JAVA日期处理类详解

    JAVA日期处理类详解

    这篇文章主要介绍了Java实现的日期处理类,结合完整实例形式分析了Java针对日期的获取、运算、转换等相关操作技巧,需要的朋友可以参考下
    2021-08-08
  • Java如何解决发送Post请求报Stream closed问题

    Java如何解决发送Post请求报Stream closed问题

    这篇文章主要介绍了Java如何解决发送Post请求报Stream closed问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Mybatis-Plus实现公共字段自动赋值的方法

    Mybatis-Plus实现公共字段自动赋值的方法

    这篇文章主要介绍了Mybatis-Plus实现公共字段自动赋值的方法,涉及到通用字段自动填充的最佳实践总结,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07
  • 浅析Java内存模型与垃圾回收

    浅析Java内存模型与垃圾回收

    下面小编就为大家带来一篇浅析Java内存模型与垃圾回收。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧,祝大家游戏愉快哦
    2016-05-05
  • Java中关于int和Integer的区别详解

    Java中关于int和Integer的区别详解

    本篇文章小编为大家介绍,在Java中 关于int和Integer的区别详解,需要的朋友参考下
    2013-04-04

最新评论