使用Java8中的Optional类来消除代码的Null检查


主题列表:juejin, github, smartblue, cyanosis, channing-cyan, fancy

贡献主题:https://github.com/xitu/juejin-markdown-themes

theme: juejin
highlight: juejin

1.0 Optional 入门

引入了一个名为 java.util.Optional<T>的新的类。这种方式对可能缺失的值建模, 而不是直接将null赋值给变量所带来的好处。 反思的是:如何在你的域模型中使用optional值。去设计更好的API—— 用户只需要阅读方法签名就能知道它是否接受一个optional的值

  • 使用新的类意味着,如果你知道一个人可能有也可能没有车,那么Person类内部的car变量就不应该声明为Car,遭遇某人没有车时把null引用赋值给它,而是应该像如下图那样直接将其声明为Optional类型。
  1. 变量存在时,Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空” 的Optional对象,由方法Optional.empty()_(静态工厂方法)返回。
  2. null引用和Optional.empty()本质的区别:
    从语义上是相同,但是实际中它们之间的差别非常大:如果你尝试解引用一个null,一定会触发NullPointerExcekognption,不过使用 Optional.empty()不会,它是Optional类的一个有效对象,多种场景都能调用,

比如在声明变量时使用的是Optional<Car>类型,而不是Car类型,这句声明非常清楚地表明了这里发生变量缺失是允许的。与此相反,使用Car这样的类型,可能将变量赋值为null,这意味你只能依赖你对业务模型的理解,判断一个null是否属于该变量的防止NullPointerExcekognption;

2.0 使用Optional重新进行建模

@Data
public class Insurance {
    private String name;
}

@Data
public class Car {
    private Optional<Insurance> insurance;
}
@Data
public class Person {
    private Optional<Car> car;
}

代码中person引用的是Optional<Car>, car引用的是Optional<Insurance>,这样的的模型表达person可能拥有也可能没有car的情形,car可能进行了Insurance,也可能没有Insuranceinsurance公司的名称被声明成String类型,而不是Optional- <String>,声明为insurance公司的类型必须提供公司名称。使用这种方式, 一旦解引用insurance公司名称时发生NullPointerException,能非常确定地知道出错的原因,不再需要为其添加null的检查,因为null的检查只会掩盖问题,并未真正地修复问题。 始终如一地使用Optional,能非常清晰地界定出变量值的缺失是结构上的问题,还是算法上的缺陷,抑或是数据中问题。另外,引入Optional类的意图并非要消除每一个null引用。是为更好地设计出普适的API, 让程序员看到方法签名,就能了解它是否接受一个Optional的值。

3.0 应用Optional的模式

3.1创建Optional对象

实例化optional对象的方法:

  1. empty() :静态工厂方法,创建一个空的Optional对象
Optional<String> emptyOpt = Optional.empty();

如果对emptyOpt变量调用isPresent()方法会返回false,调用get()方法抛出NullPointerException异常。

  1. of()创建一个非空的Optional对象的静态工厂方法

    String str = "Hello";
    Optional<String> notNullOpt = Optional.of(str)
    
  2. ofNullable() 方法接收一个可以为null的值:

    Optional<String> nullableOpt = Optional.ofNullable(str);
    
3.2使用map从 Optional对象中提取和转换值
 // 通常
String roleId = null;
if(user != null){
    roleId = user.getRoleId();
}
// Optional
Optional<User> userOpt = Optional.ofNullable(user);
Optional<String> roleIdOpt = userOpt.map(User::g

map操作会将提供的函数应用于流的每个元素。把Optional对象看成一种特殊的集合数据,它至多包含一个元素。如果Optional包含一个值,那函数就将该值作为参数传递给map,对该值进行转换。如果Optional为空,就什么也不做。下图把一个将正方形 换为三角形的函数,分别传递给正方形和Optional正方形流的map方法之后的结果。

3.3 使用flatMap链接 Optional对象

编译无法通过
原因:optPerson是Optional类型的变量, 调用map方法应该没有问题。但getCar返回的是一个Optional类型的对象(Optional<Optional<Car>> car = optPerson.map(Person::getCar); ),因此,它对getInsurance的调用是非法的,因为最外层的optional对象包含了另一个optional 对象的值,也就是出现了嵌套式optional 结构。

解决:就是让它能够接受一个函数(新流)作为参数

flatMap方法会将这种两层的Optional对象转换为包含三角形的单一Optional对象。所以就要先理解他们的区别。

3.3.4 Stream的Map与flatMap的区别
  • 相同:输入都是stream的每一个元素
  • 异点
  1. map的输出对应一个元素,必然是一个元素(null也是要返回)
  2. flatmap是0或者多个元素(为null的时候其实就是0个元素)。

Optionalmapflatmap
map是把结果自动封装成一个Optional,但是flatmap需要你自己去封装。

Optional内的Person也被转换成了一个两层的Optional对象,最终它们会被flagMap操作合并。简单地看成把两个Optional对象结合在一起,如果其中有一个对象为空,就构成一个空的Optional对象。

// 提供一个能访问声明为Optional、变量值可能缺失的接口
public Optional<Car> getCarAsOptional() {
	return Optional.ofNullable(car);
}

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

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

  1. orElse():遇空的Optional变量时,如果有值就返回,否则返回一个给定的值作为默认值

    String str = "hello";
    Optional<String> strOpt = Optional.of(str);
    String orElseResult = strOpt.orElse("hello word");
    
  2. orElseGet():与orElse()方法作用类似,区别在于生成默认值的方式不同。该方法接受一个Supplier<? extends T>函数式接口参数,用于生成默认值

    String orElseGet = strElseGet(()->"hello")
    
  3. orElseThrow():与前面介绍的get()方法类似,当值为null时调用这两个方法都会抛出NullPointerException异常,区别在于该方法可以指定抛出的异常类型。

    String orElseThrow = strOpt.orElseThrow(
        ()-> new IllegalArgumentException("Argument 'str' cannot be null or blank.")
    )
    
  4. ifPresent(),该方法接收一个Consumer<? super T>函数式接口,一般用于将信息打印到控制台,如果值存在,就执行使用该值的方法调用,否则什么也不做

    Optional<String> strOpt = Optional.of("hellp");
    strOpt.ifPresent(System.out::println)
    
  5. filer() 可用于判断Optional对象是否满足给定条件,一般用于条件过滤:

    Optional<String> optional = Optional.of("newhzong@qq.com");
    optional = optional.filter(str - >str.contains("164"));
    

  如果filter()方法中的Lambda表达式成立,filter()方法会返回当前Optional对象值   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值