前言

Java 8中引入了 Optional 类来解决 NullPointerException 与繁琐的 null 查看,该类首次呈现在 Guava。Java 8 才成为类库中的一部分。

入门

Optional 是一个封装值的类,用于保留类型为 T 的值;实质上,Optional 就是一个容器。

举例来说,一个人可能有车也可能没有,那么 Person 类外部 car 变量就不应该申明为 Car,当变量存在时,Optional 类只是对 Car 的简略封装。变量不存在时,会应用 Optional.empty() 办法返回空的 Optional 对象。如下所示:

然而 null 援用和 Optional.empty() 有什么本质区别?从语义上,它们能够当成一回事儿,但实际上差异十分大:如果尝试解援用一个 null,肯定会触发 NullPointerException,不过应用 Optional.empty() 是一个无效对象。

上面咱们来看一下 Optional 提供的性能。

创立

说到 Optional 的性能,咱们首先要理解 Optional 实例的创立。

空Optional

正如前文提到,你能够通过动态工厂办法 Optional.empty,创立一个空的 Optional 对象:

Optional<Car> option = Optional.empty();

因为 empty() 自身代表的就是空对象,所以调用 get 办法会抛出 NoSuchElementException 异样。

非空Optional

你还能够应用动态工厂办法 Optional.of ,根据一个非空值创立一个 Optional 对象:

Optional<Car> optional = Optional.of(car);

如果 car 是一个 null,这段代码会立刻抛出一个 NullPointerException,而不是等到你试图拜访 car 的属性值时才返回一个谬误。

可为null的Optional

最初,应用动态工厂办法 Optional.ofNullable,你能够创立一个容许 null 值的 Optional 对象:

Optional<Car> optional = Optional.ofNullable(car);

如果 carnull,那么失去的 Optional 对象就是个空对象。咱们能够查看一下它的实现原理:

public static <T> Optional<T> ofNullable(T value) {    return value == null ? empty() : of(value);}

依据它的实现形式,咱们可知,传入的值是空值时,会返回 Optional.empty() 空对象。这有利于咱们封装那些可能为 null 的值。例如,有一个 Map<String, Object> 实例,拜访 key 索引时,如果没有与 key 关联的值,则会返回一个 null。因而,咱们能够应用 Optional.ofNullable 办法封装返回值:

Optional<Object> value = Optional.ofNullable(map.get("key"));// 能够由上代码代替 Object value = map.get("key");

这样能够将潜在的 null 隐患替换为空的 Optional 对象。

操作

咱们创立了 Optional 实例后,须要对该实例进行操作。

isPresent & get

Optional 类中,isPresent 办法对 Optional 实例进行判断,是否蕴含值,如果存在值,就返回 true,否则返回 false;与之绝对的是 isEmpty 办法Optional 类中还有 get 办法,它是用来获取 Optional 实例中的值。

Optional<String> optional = Optional.of("is present");if (optional.isPresent()) {    System.out.println("the value is " + optional.get());}

isPresentget 个别组合应用来防止 NullPointerException

public boolean isPresent() {    return value != null;}public T get() {    if (value == null) {        throw new NoSuchElementException("No value present");    }    return value;}

从源码中可看出,get 办法在取值时,要进行判空操作,如果不应用 isPresent 办法,可能会呈现空指针异样。然而这种形式和在代码中if(null != value) 没有区别,因而咱们要尽量避免应用该组合。

ifPresent

除了 isPresent 的简洁办法,Optional 还提供了接管函数式参数的接口 ifPresent

public void ifPresent(Consumer<? super T> action) {    if (value != null) {        action.accept(value);    }}

该办法会接管一个消费型函数。如果 Optional 实例中的值不为空,则调用 Consumeraccept 办法对 value 进行生产,若为空则不做解决。下面的例子能够应用 ifPresent 重写:

Optional<String> optional = Optional.of("is present");optional.isPresent((val) -> System.out.println("the value is " + val));

orElse

咱们还能够应用 orElse 办法读取 Optional 中的值。

public T orElse(T other) {    return value != null ? value : other;}

应用这种形式能够定义一个默认值,这种形式当遭逢 Optional 中的变量为空时,默认值会作为该办法的返回值。

String optGet = null;String orElse = Optional.ofNullable(optGet).orElse("Default");/** * 输入后果: * Default */

orElseGet

如果该办法不够,咱们能够应用另一种形式 orElseGet

public T orElseGet(Supplier<? extends T> supplier) {    return value != null ? value : supplier.get();}

该办法与 orElse 的区别就是值不存在时,调用实现 Supplier 接口的办法或Lambda表达式来返回默认值。

String optGet = null;String orElse = Optional.ofNullable(optGet).orElse(() -> "Default");/** * 输入后果: * Default */

orElseThrow

orElseThrow办法是在有值时返回其值,无值的时候会抛出由 Supplier 创立的异样。咱们看一下它的实现原理:

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {    if (value != null) {        return value;    } else {        throw exceptionSupplier.get();    }}

orElseThrow 原理中,会传入一个Lambda表达式或办法,如果值不存在来抛出异样:

class NoValueException extends RuntimeException {    public NoValueException() {        super();    }    @Override    public String getMessage() {        return "No value present in the Optional instance";    }}public static Integer orElseThrow() {    return (Integer) Optional.empty().orElseThrow(NoValueException::new);}public static void main(String[] args) {    orElseThrow();}/** * 控制台会抛出异样: * Exception in thread "main" xx.NoValueException: No value present in the Optional instance */

orElseThroworElseGet 的区别就是一个在无值的时候抛出异样,一个在无值的时候应用Lambda表达式来实现默认值。

orElseThrow 只是在无值的时候抛出异样,那自身会抛出异样的办法呢?

当初,咱们拿 Integer.parseInt(String) 做个例子:

public static Integer toInt(String s) {    try {        // 如果String能转换为对应的Integer,将其封装在Optional对象中返回        return Integer.parseInt(s);    } catch (NumberFormatException e) {        return null;    // 返回null 或者抛出异样    }}

在将 String 转换为 int 时,如果无奈解析到对应的整型,该办法会抛出 NumberFormatException 异样。咱们在该办法中应用 try/catch 语句捕捉了该异样,不能应用 if 条件判断来管制一个变量的值是否为空。

这时,咱们能够应用 Optional 类,来对无奈转换的 String 时返回的非法值进行建模,因而,咱们能够对上述办法进行改良:

public static Optional<Integer> toInt(String s) {    try {        // 如果String能转换为对应的Integer,将其封装在Optional对象中返回        return Optional.of(Integer.parseInt(s));    } catch (NumberFormatException e) {        return Optional.empty();    // 否则返回一个空的 Optional 对象    }}

这种返回 Optional 的形式实用很多办法,咱们只须要获取被 Optional 包装的值的实例即可。

map

Optional 提供了 map 办法用于从对象中提取信息,它的工作原理如下:

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {    Objects.requireNonNull(mapper);    if (!isPresent())        return empty();    else {        return Optional.ofNullable(mapper.apply(value));    }}

map 操作会将提供的函数利用于流的每个元素。咱们能够把 Optional 对象看成一种非凡的汇合数据,它至少蕴含一个元素。如果 Optional 蕴含一个值,那通过实现了 Function 接口的 Lambda 表达式对值进行转换。如果不相熟 Function 接口,能够参考这篇文章。map 办法示例如下:

class Car {    private String name;    private String type;    ...省略getter与setter...}Optional<Car> optional = Optional.ofNullable(car);Optional<String> name = optional.map(Car::getName);

flatMap

咱们能够应用 map 办法来从被 Optional 类包装的 Person 类中获取 Car 的名称:

class Person {    private Optional<Car> car;    public Person(Car car) {        this.car = Optional.of(car);    }    ...省略getter与setter...}Person person = new Person(new Car());Optional<Person> optPerson = Optional.of(person);Optional<String> name = optPerson.map(Person::getCar)    .map(Car::getName);

可怜的是,这段代码无奈通过编译。为什么呢?optPersonOptional<Person> 类型的变量,调用 map 办法应该没有问题。但 getCar 返回的是一个 Optional<Car> 类型的对象,这意味着 map 操作的后果是一个 Optional<Optional<Car>> 类型的对象。因而,它对 getName 的调用是非法的,因为最外层的 optional 对象蕴含了另一个 optional 对象的值,而它当然不会反对 getName 办法。

所以,咱们应用 flatMap 办法。该办法承受一个函数作为参数,这个函数的返回值是另一个流。

public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) {    Objects.requireNonNull(mapper);    if (!isPresent()) {        return empty();    } else {        @SuppressWarnings("unchecked")        Optional<U> r = (Optional<U>) mapper.apply(value);        return Objects.requireNonNull(r);    }}

参照 map 函数,应用 flatMap 重写上述的示例:

Optional<String> name = optPerson.flatMap(Person::getCar).map(Car::getName);

filter

有时候咱们须要对 Optional 中的值进行过滤,取得咱们须要的后果,咱们就能够应用 filter 办法:

public Optional<T> filter(Predicate<? super T> predicate) {    Objects.requireNonNull(predicate);    if (!isPresent())        return this;    else        return predicate.test(value) ? this : empty();}

该办法承受 Predicate 谓词作为参数。如果 Optional 对象的值存在,并且合乎谓词的条件,即操作后果为truefilter 办法不做任何扭转并返回其值;否则就将该值过滤掉并返回一个空的 Optional 对象。

Optional<String> optionalS = Optional.of("13846901234");optionalS = optionalS.filter(s -> s.contains("138"));/** * 上述 `filter` 办法满足条件能够返回同一个Optional,否则返回空Optional */

总结

Java 8引入的 java.util.Optional<T> 让咱们以函数式编程的形式解决 null,避免空指针异样;并反对多种形式用于操作值,比方:mapflatMapfilter,这样能够摈弃嵌套的 if-else 代码块,设计更好的 API,代码的可读性也大大提高,然而如果在域模型中应用 Optional,因为没有实现 Serializable 接口,不能进行实例化,也不能作为类的字段。

如果想要查看更多文章,请关注公众号「海人的博客」