1965年,英国一位名为Tony Hoare的计算机科学家在设计ALGOL W语言时提出了null引用的想法。ALGOL W是第一批在堆上分配记录的类型语言之一。Hoare选择null引用这种方式,“只是由于这种方法实现起来很是容易”。虽然他的设计初衷就是要“经过编译器的自动检测机制,确保全部使用引用的地方都是绝对安全的”,他仍是决定为null引用开个绿灯,由于他认为这是为“不存在的值”建模最容易的方式。不少年后,他开始为本身曾经作过这样的决定然后悔不已,把它称为“我价值百万的重大事物”。实际上,Hoare的这段话低估了过去五十年来数百万程序员为修复空引用所耗费的代价。近十年出现的大多数现代程序设计语言1,包括Java,都采用了一样的设计方式,其缘由是为了与更老的语言保持兼容,或者就像Hoare曾经陈述的那样,“仅仅是由于这样实现起来更加容易”。
public class Person { private Car car; public Car getCar() { return car; } } 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没有车,就会形成空指针异常。java
简单来讲就是在须要的地方添加null检查程序员
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"; }
上述代码不具有扩展性,同时还牺牲了代码的可读性。segmentfault
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类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空”的Optional对象,由方法Optional.empty()返回。Optional.empty()方法是一个静态工厂方法,它返回Optional类的特定单一实例。函数
引入Optional类的意图并不是要消除每个null引用,相反的是,它的目标是帮助开发者更好地设计出普适的API。工具
正如前文已经提到,你能够经过静态工厂方法Optional.empty,建立一个空的Optional对象:性能
Optional<Car> optCar = Optional.empty();
你还可使用静态工厂方法Optional.of,依据一个非空值建立一个Optional对象:优化
Optional<Car> optCar = Optional.of(car);
若是car是一个null,这段代码会当即抛出一个NullPointerException,而不是等到你试图访问car的属性值时才返回一个错误。spa
最后,使用静态工厂方法Optional.ofNullable,你能够建立一个容许null值的Optional对象:设计
Optional<Car> optCar = Optional.ofNullable(car);
若是car是null,那么获得的Optional对象就是个空对象。
从对象中提取信息是一种比较常见的模式。
String name = null; if(insurance != null){ name = insurance.getName(); } 为了支持这种模式,Optional提供了一个map方法。 Optional<Insurance> optInsurance = Optional.ofNullable(insurance); Optional<String> name = optInsurance.map(Insurance::getName);
使用流时,flatMap方法接受一个函数做为参数,这个函数的返回值是另外一个流。 这个方法会应用到流中的每个元素,最终造成一个新的流的流。可是flagMap会用流的内容替换每一个新生成的流。换句话说,由方法生成的各个流会被合并或者扁平化为一个单一的流。
public String getCarInsuranceName(Optional<Person> person) { return person.flatMap(Person::getCar) .flatMap(Car::getInsurance) .map(Insurance::getName) .orElse("Unknown"); }
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(); } }
filter方法接受一个谓词做为参数。若是Optional对象的值存在,而且它符合谓词的条件, filter方法就返回其值;不然它就返回一个空的Optional对象。
Insurance insurance = ...; if(insurance != null && "CambridgeInsurance".equals(insurance.getName())){ System.out.println("ok”); } Optional<Insurance> optInsurance = ...; optInsurance.filter(insurance -> "CambridgeInsurance".equals(insurance.getName())) .ifPresent(x -> System.out.println("ok"));
Optional类中的方法进行了分类和归纳:
Optional<Object> value = Optional.ofNullable(map.get("key"));
每次你但愿安全地对潜在为null的对象进行转换,将其替换为Optional对象时,均可以考虑使用这种方法。
public static Optional<Integer> stringToInt(String s) { try { return Optional.of(Integer.parseInt(s)); } catch (NumberFormatException e) { return Optional.empty(); } }
咱们的建议是,你能够将多个相似的方法封装到一个工具类中,让咱们称之为OptionalUtility。经过这种方式,你之后就能直接调用OptionalUtility.stringToInt方法,将String转换为一个Optional<Integer>对象,而再也不须要记得你在其中封装了笨拙的 try/catch的逻辑了。
public int readDuration(Properties props, String name) { String value = props.getProperty(name); if (value != null) { try { int i = Integer.parseInt(value); if (i > 0) { return i; } } catch (NumberFormatException nfe) { } } return 0; } // 优化版本 public int readDuration(Properties props, String name) { return Optional.ofNullable(props.getProperty(name)) .flatMap(OptionalUtility::stringToInt) .filter(i -> i > 0) .orElse(0); }
这一章中,你学到了如下的内容。