暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Java Optional 用法

程序员随想 2021-06-21
327

1965年,英国一位名为Tony Hoare的计算机科学家在设计ALGOL W语言时提出了null引用的想法。ALGOL W是第一批在堆上分配记录的类型语言之一。Hoare选择null引用这种方式,“只是因为这种方法实现起来非常容易”。虽然他的设计初衷就是要“通过编译器的自动检测机制,确保所有使用引用的地方都是绝对安全的”,他还是决定为null引用开个绿灯,因为他认为这是为“不存在的值”建模最容易的方式。很多年后,他开始为自己曾经做过这样的决定而后悔不迭,把它称为“我价值百万的重大失误”。我们已经看到它带来的后果——程序员对对象的字段进行检查,判断它的值是否为期望的格式,最终却发现我们查看的并不是一个对象,而是一个空指针,它会立即抛出一个让人厌烦的NullPointerException异常。

如何避免空指针异常

假设你需要处理下面这样的嵌套对象,这是一个拥有汽车及汽车保险的客户。

    import java.util.*;


    public class Person {
        private Car car;
    public Car getCar() {
    return car;
        }
    }


    import java.util.*;
    public class Car {
        private Insurance insurance;
    public Insurance getInsurance() {
    return insurance;
    }
    }


    public class Insurance {
        private String name;
    public String getName() {
    return name;
    }
    }

    那么,下面这段代码存在怎样的问题呢?

      public String getCarInsuranceName(Person person){
        return person.getCar().getInsurance().getName();
      }

      很显然,如果person引用的car的值是null,则会导致运行时出现一个NullPointerException。


      • 采用防御式检查减少NullPointerException


        //深层质疑
        public String getCarInsuranceName(Person person){
        if(person != null){
        Car car = person.getCar();
        if(car != null){
        Insurance insurance = car.getInsurance();
        if(insurance !=null){
        return insurance.getName();
        }
        }
        }
        return "Unknown";
        }
         
         //过多的退出语句
         public String getCarInsuranceName(Person person){
        if(person == null){
        return "Unknown";
        }
        Car car = person.getCar();
        if(car == null){
        return "Unknown";
        }
        Insurance insurance = car.getInsurance();
        if(insurance == null){
        return "Unknown";
        }
        return insurance.getName();
        }

        Optional类入门

        Java 8中引入了一个新的类java.util.Optional<T>。这是一个封装Optional值的类

        变量存在时,Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空”的Optional对象,由方法Optional.empty()返回。Optional.empty()方法是一个静态工厂方法,它返回Optional类的特定单一实例。

          //使用Optional重新定义Person/Car/Insurance的数据模型
          import java.util.*;
          public class Person {
              private Optional<Car> car;
          public Optional<Car> getCar() {
          return car;
          }
           }
           import java.util.*;
           public class Car {
              private Optional<Insurance> insurance;
          public Optional<Insurance> getInsurance() {
          return insurance;
          }
           }
           
           public class Insurance {
              private String name;
          public String getName() {
          return name;
          }
           }

          应用Optional

          • 声明一个空的Optional

            Optional<Car> optCar = Optional.empty();
            • 依据一个非空值创建Optional

              Optional<Car> optCar = Optional.of(car);
              • 可接受null的Optional

                //如果car是null,那么得到的Optional对象就是个空对象。
                Optional<Car> optCar = Optional.ofNullable(car);
                • 使用map从Optional对象中提取和转换值

                  String name = null;
                  if(insurance != null){
                  name = insurance.getName();
                  }
                  Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
                  Optionnal<String> name = optInsurance.map(Insurance::getName);ap(Insurance::getName);
                  • 使用flatMap链接Optional对象

                    public String getCarInsuranceName(Optional<Person> person){
                    return person.flatMap(Person::getCar)
                    .flatMap(Car::getInsurance)
                    .map(Insurance::getName)
                    .orElse("Unknown");
                    }


                    Stream和Optional的flagMap方法对比



                    使用Optional解引用串接的Person/Car/Insurance


                    默认行为及解引用Optional对象

                    Optional类提供了多种方法读取Optional实例中的变量值。

                    • get()是这些方法中最简单但又最不安全的方法。如果变量存在,它直接返回封装的变量值,否则就抛出一个NoSuchElementException异常。所以,除非你非常确定Optional变量一定包含值,否则使用这个方法是个相当糟糕的主意。此外,这种方式即便相对于嵌套式的null检查,也并未体现出多大的改进。

                    • orElse(T other)是我们在代码清单10-5中使用的方法,正如之前提到的,它允许你在Optional对象不包含值时提供一个默认值。

                    • orElseGet(Supplier<? extends T> other)是orElse方法的延迟调用版,Supplier方法只有在Optional对象不含值时才执行调用。如果创建默认值是件耗时费力的工作,你应该考虑采用这种方式(借此提升程序的性能),或者你需要非常确定某个方法仅在Optional为空时才进行调用,也可以考虑该方式(这种情况有严格的限制条件)。

                    • orElseThrow(Supplier<? extends X> exceptionSupplier)和get方法非常类似,它们遭遇Optional对象为空时都会抛出一个异常,但是使用orElseThrow你可以定制希望抛出的异常类型。

                    • ifPresent(Consumer<? super T>)让你能在变量值存在时执行一个作为参数传入的方法,否则就不进行任何操作。

                    两个Optional对象的组合

                      public Insurance findCheapestInsurance(Person person, Car car){
                      //
                      //返回最便宜的保险公司
                      return cheapestCompany;
                       }

                      public Optional<Insurance> nullSafeFindCheapestInsurance(Optional<Person> person,
                      Optional<Car> car){
                      if(person.isPresent() && car.isPresent()){
                      return Optional.of(findCheapestInsurance(person.get(),car.get());
                      }else{
                      return Optional.empty();
                      }
                      }
                      //以不解包的方式组合两个Optional对象
                      public Optional<Insurance> nullSafeFindCheapestInsurance(Optional<Person> person, 
                      Optional<Car> car){
                      return person.flatMap(p-> car.map(c->findCheapestInsurance(p,c)));
                      }

                      使用filter剔除特定的值

                        Insurance insurance = ...;
                        if(insurance != null && "sss".equals(insurance.getName()){
                        System.out.println("ok");
                        }


                        //Optional对象的filter方法重构
                        Optional<Insurance> optInsurance= ...;
                        optInsurance.filter(insurance -> "sss".equals(insurance.getName()))
                        .ifPresent(x-> System.out.println("ok"));


                        Optional类的方法



                        文章转载自程序员随想,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                        评论