软件编程
位置:首页>> 软件编程>> java编程>> Java8中Optional类的使用说明

Java8中Optional类的使用说明

作者:永动的图灵机  发布时间:2023-07-25 13:31:32 

标签:java8,optional类,新特性

    简介

    optional类是java8中引入的针对NPE问题的一种优美处理方式,源码作者也希望以此替代null。

    历史

    1965年,英国一位名为Tony Hoare的计算机科学家在设计ALGOL W语言时提出了null引用的想法。Hoare选择null引用这种方式,“只是因为这种方法实现起来非常容易”。很多年后,他开始为自己曾经做过这样的决定而后悔不迭,把它称为“我价值百万的重大失误”。我们已经看到它带来的后果——程序员对对象的字段进行检查,判断它的值是否为期望的格式,最终却发现我们查看的并不是一个对象,而是一个空指针,它会立即抛出一个让人厌烦的NullPointerException异常[1]。

    null带来的种种问题

    • 错误之源。

    NullPointerException是目前Java程序开发中最典型的异常。

    • 代码膨胀。

    它让你的代码充斥着深度嵌套的null检查,代码的可读性糟糕透顶。

    • 自身是毫无意义的。

    null自身没有任何的语义,尤其是,它代表的是在静态类型语言中以一种错误的方式对缺失变量值的建模。

    • 破坏了Java的哲学。

    Java一直试图避免让程序员意识到指针的存在,唯一的例外是:null指针。

    • 在Java的类型系统上开了个口子。

    null并不属于任何类型,这意味着它可以被赋值给任意引用类型的变量。这会导致问题,原因是当这个变量被传递到系统中的另一个部分后,你将无法获知这个null变量最初的赋值到底是什么类型。

    方案

    汲取Haskell和Scala的灵感,Java 8中引入了一个新的类java.util.Optional

    场景引入

    首先我们引入一个常见的两个场景


    /**
    * >1 引入,常规判断一个学生的学校是不是公立学校,判断是否成年
    */
    public static boolean checkIsPublicV1(Student student) {
       if (student != null) {
           School school = student.getSchool();
           if (school != null) {
               return school.isPublicFlag();
           }
       }
       throw new RuntimeException("参数异常");
    }
    public static String getAdultV1(Student student) {
       if (student != null) {
           int age = student.getAge();
           if (age > 18) {
               return student.getName();
           }
       }
       return "无";
    }

    上述方式是我们常见的判读流程,optional就是针对每次空判断导致的代码欣赏性问题进行了一套解决方案

    方法说明

    Java8中Optional类的使用说明

    构造函数

    • Optional(T var1)

    源码


    private final T value;
    private Optional() {
       this.value = null;
    }
    private Optional(T var1) {
       this.value = Objects.requireNonNull(var1);
    }

    从源码可知,optional的构造器私有,不能直接创建,只能通过类中的其他静态方法创建,optional构造器支持一个泛型入参,且改参数不能为空

    创建Optional对象

    • 声明一个空的Optional: Optional

    • 依据一个非空值创建Optional: Optional

    • 可接受null的Optional: Optional

    源码

    empty()源码


    private static final Optional

    从源码可知,optional类中维护了一个null值的对象,使用empty静态方法即可返回该空值对象

    of(T var0)源码


    public static

    返回常见的有参构造对象,注意由于构造器要求入参不能为空,因此of方法的入参为空的话,依然会报NPE异常

    ofNullable(T var0)源码


    public static

    这个方法是对of方法的补强,当ofNullable方法的入参不为空是正常返回构造对象,当入参为空时,返回一个空值的optional对象,而不会抛出异常。

    ofNullable方法大部分场景优于of的使用。

    null引用和Optional.empty()有什么本质的区别吗?从语义上,你可以把它们当作一回事儿,但是实际中它们之间的差别非常大:如果你尝试解引用一个null,一定会触发NullPointerException,不过使用Optional.empty()就完全没事儿,它是Op-tional类的一个有效对象,多种场景都能调用,非常有用。

    举例


    public static void testOptionalBuild() {
       // 1. 无法直接new 'Optional()' has private access in 'java.util.Optional'
       // Optional

    使用map从Optional对象中提取和转换值

    • Optional map(Function

    源码


    public Optional map(Function

    当optional包裹的值为空时直接放回空对象,否则执行入参中的Function.apply方法

    使用flatMap链接Optional对象

    • Optional flatMap(Function<? super T, Optional> var1)

    源码


    public Optional flatMap(Function<? super T, Optional> var1) {
       Objects.requireNonNull(var1);
       return !this.isPresent() ? empty() : (Optional)Objects.requireNonNull(var1.apply(this.value));
    }

    与map几乎一致。注意的是,入参的Function.apply方法中,返回类型为optional类型

    举例


    public static void testOptionalMap() {
       Student student1 = getDefaultStudent();
       Student student2 = getBackStudent();
       // map
       String school1 = Optional.ofNullable(student1).map(i -> i.getName()).orElse("无名");
       String school2 = Optional.ofNullable(student2).map(i -> i.getName()).orElse("无名");
       System.out.println("school1: " + school1 + "| school2: " + school2);
       // flapMap 链式
       String school3 = Optional.ofNullable(getOptionalStudent()).flatMap(i -> getOptionalStudent()).flatMap(i->i.getSchool()).map(i->i.getSchoolName()).orElse("没上大学");
       System.out.println("school3: " + school3);
    }

    默认行为及解引用Optional对象1

    • T orElse(T var1)

    • T orElseGet(Supplier

    • T orElseThrow(Supplier

    注:这三个方法方法不是静态方法,因此需要通过实例对象调用,一般跟在方法ofNullable后用于处理空值或返回值

    源码

    orElse源码


    public T orElse(T var1) {
       return this.value != null ? this.value : var1;
    }

    当optional中的包裹值不为空时返回包裹的值,若为空则返回orElse中的入参值

    orElseGet源码


    public T orElseGet(Supplier

    与上个方法类似,当optional中的包裹值不为空时返回包裹的值,若为空执行orElseGet中的Supplier方法

    orElseThrow源码


    public

    类似的,当optional中的包裹值不为空时返回包裹的值,若为空抛出orElseThrow中的Supplier.get的异常方法

    举例


    public static void testOptionalOrElse() {
       // orElse
       Student stu = getDefaultStudent();
       Student backStudent = getBackStudent();
       Student realStu1 = Optional.ofNullable(stu).orElse(backStudent);
       System.out.println(realStu1);
    // orElseGet
       Student realStu2 = Optional.ofNullable(stu).orElseGet(()-> getBackStudent());
       System.out.println(realStu2);
    // orElseGet
       Student realStu3 = Optional.ofNullable(stu).orElseThrow(()-> new RuntimeException("学生不存在"));
       System.out.println(realStu3);
    }

    默认行为及解引用Optional对象2

    • boolean isPresent()

    • void ifPresent(Consumer

    源码


    isPresent()源码
    public boolean isPresent() {
       return this.value != null;
    }

    用户判断optional包裹的值是否为空,返回布尔值

    ifPresent(Consumer var1)源码


    public void ifPresent(Consumer

    用户处理optional包裹的值不为空时,继续处理入参中Consumer.accept的方法。类似于 if(var!=null) {do sth}

    举例


    public static void testOptionalIfPresent() {
       // isPresent()
       Student student1 = getDefaultStudent();
       Student student2 = getBackStudent();
       boolean b1 = Optional.ofNullable(student1).isPresent();
       boolean b2 = Optional.ofNullable(student2).isPresent();
       System.out.println("b1: " + b1 + "| b2: " + b2);
    // isPresent(Consumer)
       Optional.ofNullable(student2).ifPresent(i-> acceptStudent(i, LocalDate.now()));
    }

    使用filter剔除特定的值

    • Optional filter(Predicate

    源码


    public Optional

    用于对optional对象的过滤,当optional包裹的值不为空时返回该值,否则执行filter入参的Predicate.test方法

    举例


    public static void testOptionalFilter() {
       Student student1 = getDefaultStudent();
       Student student2 = getBackStudent();
       System.out.println(student1);
       System.out.println(student2);
       Student student3 = Optional.ofNullable(student1).filter(i -> i.getAge() > 18).orElse(getBackStudent());
       Student student4 = Optional.ofNullable(student2).filter(i -> i.getAge() > 18).orElse(getBackStudent());
       System.out.println(student3);
       System.out.println(student4);
    }

    实战

    关于optional类的说明大致已经讲完,再回到开始的时候,提到的场景引入,结合optional进行改造


    /**
    * 实战1
    * 针对原来的checkIsPublicV1进行改造
    */
    public static boolean checkIsPublicV2(Student student) {
       return Optional.ofNullable(student).map(i -> i.getSchool()).map(i -> i.isPublicFlag()).orElseThrow(() -> new RuntimeException("参数异常"));
    }
    /**
    * 实战1
    * 针对原来的getAdultV1进行改造
    */
    public static String getAdultV2(Student student) {
       return Optional.ofNullable(student).filter(i->i.getAge()>18).map(i->i.getName()).orElseGet(()->getDefaultStudent().getName());
    }

    附:

    补充代码


    public static void main(String[] args) {
       //逐个放开
       // 引入
    //        System.out.println(checkIsPublicV1(stu2));
    //        System.out.println(getAdultV1(stu2));
       // optional方法
    //        testOptionalBuild();
    //        testOptionalOrElse();
    //        testOptionalIfPresent();
    //        testOptionalMap();
    //        testOptionalFilter();
       // 实战
    //        System.out.println(getAdultV2(stu3));
    //        System.out.println(checkIsPublicV2(stu3));
    }
    /**========模型数据=======**/
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    static class Student {
       private String name;
       private int age;
       private School school;
    }
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    static class School {
       private String schoolName;
       private boolean publicFlag;
    }
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    static class StudentOpt {
       private String name;
       private int age;
       private Optional

    [1] 参考自java8实战

    详细源码,请参考:github.com/chetwhy/clo…

    来源:https://juejin.cn/post/7025925484462473246

    0
    投稿

    猜你喜欢

    手机版 软件编程 asp之家 www.aspxhome.com