Files
Hui-s-notebook/用Optional取代null.md
2023-09-10 10:50:53 +08:00

3.6 KiB
Raw Blame History

如何为缺失的值建模

采用防御式检查减少 Null-PointerException

在需要的地方添加 null 的检查, 每一次不确定一个变量是否为 null 时,都需要添加一个进一步的 if 块

每次遭遇 null 变量,返回一个字符串常量"Unknown"

null 带来的种种问题

  • 它是错误之源
  • 它会使你的代码膨胀
  • 它自身是毫无意义的
  • 它破坏了 java 的哲学
  • 它在 Java 的类型系统上开了个口子

其他语言中 null 的替代品

Groovy 使用安全导航符, Haskell 包含一个 Maybe 类型,本质时对 optional 值的封装, Scala 有类似的结构,叫 Opeion[T]

Optional 类入门

java. util. Optional<T>,这是一个封装 opeional 值的类

应用Optional的几种模式

创建Optional对象

  1. 声明一个空的 Optional
Optional<Car> optCar = Optional.empty();
  1. 依据一个非空值创建 Optional
Optional<Car> optCar = Optional.of(car);
// 如果car为null会立即抛出NullPointerException
  1. 可接受 null 值的 Optional
Optional<Car> optCar = Optional.ofNullable(car);

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

Optional<Insurance> optInsurance=Optional.ofNullable(insurance);
Optional<String> name=optInsurance.map(Insurance::getName);
  1. 使用 flatMap 链接 Optional 对象
public String getCarInsuranceName (Optional<Person> person
	return person.flatMap(Person::getCar)
				 .flatMap(Car::getInsurance)
				 .map(Insurance::getName)
				 .orElse ( "Unknown" );
}
  1. 使用 Optional 解引用串接的 Person/Car/Insurance 对象

flatMap(Person::getCar) 获取 Optional<Car>类型对象, 再将 Optional<Car>转换为 Optional<Insurance>。 最后则会将 Optional<Insurance>转化为 Optional<String>对象

Optional 无法序列化

默认行为及解引用Optional对象

  • get () 是最简单最不安全的方法,没有对象时抛出 NoSuchElementException 异常
  • orElseT other允许在 Optional 对象不包含值是提供一个默认方法
  • orElseGetSupplier<? extends T> other是 orElse 方法的延迟调用版, Supplier 方法只有在 Optional 对象不含值时才执行调用
  • orElseThrow(Supplier<? extends X>exceptionSupplier)可以制定希望抛出的异常类型
  • ifPresent(Consumer<? super T>)能让你在变量值存在时执行一个作为参数传入的方法,否则就不进行操作

两个 Optional 对象的组合

以不解包的方式组合两个 Optional 对象

public Optional<Insurance> nullSafeFindCheapestInsurance(Optional<Person> person, Optional<Car> car) {
  return person.flatMap(p-> car.map(c-> findCheapestInsurance(p, c)));
}

使用 filter 剔除特定的值

查看保险公司名称是否为"CambridgeInsurance"

Optional<Insurance> optInsurance=...;
optInsurance.filter(insurance->"CambridgeInsurance".equals(insurance.getName()))
			.ifPresent(x-> System.out.println("ok"));

使用Optional的实战示例

用 Optional 封装可能为 null 的值

Optional<Object> value=Optional.ofNullable(map.get("key"));

异常与 Optional 的对比

public static Optional<Integer> stringToInt(String s) {
	try{
		return Optional.of(Integer.parseInt(s));
	} catch (NulberFormatException e) {
		return Optional.empty();
	}
}

把所有内容整合起来

public int readDuration(Properties props, String name) {
  return Optional.ofNullable(props.getProperty(name))
				  .flatMap(OptionalUtility::stringToInt)
				  .filter(i-> i > 0)
				  .orElse(0);
}