java学习第1期 - Optional<T>

1,047 阅读4分钟

Optional<T> 是什么

Optional<T> 是 Java8 引入的一个容器,用于包装可能为 null 的值,它的主要目的是:

1、显示的表示“值可能不存在”

2、强制调用方处理空值的情况

关键方法

声明一个Optional

of(T value)

包装非 null 值,这是个静态方法。

Optional<String> s = Optional.of("Hello world!");

ofNullable(T value)

包装可能为 null 的值,这也是个静态方法。

Optional<String> s = Optional.ofNullable("Hello world!");

empty()

返回一个包装 null 的Optional

Optional<String> s = Optional.empty();


// 等同于 Optional<String> s = Optional.ofNullable(null);

存在性判断

isPresent()

判断是否有非null元素。

Optional<String> s = someMethod();

if (s.isPresent()) {
    // do something
}

isEmpty()

判断是否是 null 元素

Optional<String> s = someMethod();

if (s.isEmpty()) {
    // do something
}

条件执行

ifPresent(Consumer)

如果是非null就执行一些操作,是null则什么也不做,只是简单的消费者,不能return值。

Optional<String> s = someMethod();

s.ifPresent(v -> {
    System.out.println("current value: " + v);
    
    // do something with the value
});

ifPresentOrElse(Consumer)

可以同时指定null和非null两种场景的回调函数。

Optional<String> s = someMethod();

s.ifPresentOrElse(v -> {
    System.out.println("current value: " + v);

    // do something with the value
},
() -> {
    System.out.println("no value present");

    // do something else
});

获取值

get()

直接获取内部的值,如果是null,则会抛出异常。建议先 isPresent() 先检查是否存在值,再调用get()

Optional<String> s = someMethod();

if (s.isPresent()) {
    System.out.println(s.get());
}

orElse(T value)

获取内部的值,如果为null,则返回给定的默认值。

Optional<String> s = someMethod();

String s1 = s.orElse("Default value");

orElseGet(Supplier)

和 orElse 最终的效果一样。

Optional<String> s = someMethod();

String s1 = s.orElseGet(() -> someComplexMethod());

和 orElse 的区别在于:

1、orElse是不管optional内部是否为null,都会执行默认方法,适用于默认值为简单常量的场景。

2、orElseGet 会先判断optional 是否为null,当为null才会执行默认方法

所以如果这样调用就不太好,因为一定会执行复杂的方法,耗费性能:

Optional<String> s = someMethod();

// 错误演示
String s1 = s.orElse(someComplexMethod());

orElseThrow(Supplier)

获取值,如果为null,则抛出给定的异常。

Optional<String> s = someMethod();

String s1 = s.orElseThrow(() -> new RuntimeException("Error!"));

过滤和转换

filter(Predicate)

给定一个判断条件,如果满足条件,则什么也不改变;如果不满足条件,则内部元素被拦截,不会往下游走,也就是变成了 Optional.empty()。

Optional<String> s = someMethod();

// 如果 v.length() > 5 条件不成立,则 s1 变成了空集合
Optional<String> s1 = s.filter(v -> v.length() > 5);

map(Function)

这个就好理解了,改变元素的形状或者值。

// 改变形状:从 Optional<Integer> 经过演算变成 Optional<String>
Optional<Integer> s = someMethod();

Optional<String> s1 = s.map(Object::toString);

// 改变内容:元素类型不变,但是值发生改变
Optional<String> s = someMethod();

Optional<String> s1 = s.map(v -> v + "hello");

flatMap(Function)

扁平化转换,处理嵌套的Optional,可以将 Optional<Optional<String>> 转换成 Optional<String>

Optional<String> s = someMethod();

Optional<String> t = s.flatMap(v -> {
    // 这里不同,返回的不是元素的类型,而是需要再包裹Optional
    return Optional.of(v + " world!");  
});

最佳实践

只使用在方法返回值/null检查

Optional 的目的之一是强制调用方处理空值的情况,所以推荐在方法返回值的时候使用,不要在参数或者对象属性中使用。同时也可以进行null检查。

// √ 推荐用法: 方法返回值
public Optional<String> someMethod() {

    // do some work

    return Optional.empty();
}

// √ 推荐用法: null值检查
public void someMethod() {
    String s = somefun();  // 不知道s是不是null

    String t = Optional.ofNullable(s)
      .orGet("hello");     // 中间可以配合filter和map进行演算
}

不要包装集合类型

Optional 已经是一个集合,没必要在包括另外一个集合类型,意义不是很大,如果是空数据,返回空集合即可。

// 不推荐用法
public Optional<List<String>> someMethod() {

    // do some work
    List<String> list = new ArrayList<>();

    return Optional.ofNullable(list);
}

// 返回集合类型没必要用到 Optional, 当为null直接返回空集合即可
public List<String> someMethod() {

    // do some work
    List<String> list = new ArrayList<>();

    if (list == null) {
        return new ArrayList<>();
    }

    return list;
}

避免 isPresent() + get() 的啰嗦写法

这样容易写出命令式代码,不够优雅,并且Optional的提出就是为了避免繁琐的 if 判断。

// 不推荐写法
Optional<String> s = someMethod();

if (s.isPresent()) {
    System.out.println(s.get());

    // do something with the value
}

而是推荐以下写法:

1、当只是简单的消费数据,不需要return,推荐使用 ifPresent(Consumer)

Optional<String> s = someMethod();

s.ifPresent(v -> {
    System.out.println("current value: " + v);
    
    // do something with the value
});

2、需要 return 值,推荐 map + orElse/orElseGet

// 不推荐写法
public String fun() {
    Optional<String> s = someMethod();

    if (s.isPresent()) {
        System.out.println(s.get());
    
        // do something with the value
        return "anyhow" + s.get();
    }
    return "other";
}


// 推荐写法, 使用 map + orElse/orElseGet
public String fun() {
    Optional<String> s = someMethod();

    return s..map(value -> {
            System.out.println(value);
            return "anyhow" + value;
        })
        .orElse("default");  // 这里可以替换成 orElseGet
}