1、Optional使用场景
写代码时为了防止空指针的异常NullPointerException(NPE),一般会对参数进行判空处理,日常开发过程中对参数的判空校验有两种情况:
- 前端调用后端接口,后端在controller层里的接口对入参进行校验判空,一般会结合
@Validated
和@NotNull
注解; - 后端之间互相调用接口,出于某些原因未能成功调用接口,接口的返回值为null,此时如果依然对接口返回的对象调用get属性之类的方法会抛空指针异常。
Java8针对第二种空指针异常场景提出了类级别的解决方案:Optional。不用Optional,我们一般会进行如下判空处理:
Object object = getObject();
if (object != null)
{
业务逻辑,比如object.getProperties();
}
else
{
return;
}
使用Optional,上面那一坨逻辑就可以写成:
Object object = getObject();
Optional.ofNullable(object).ifPresent(业务逻辑,比如object.getProperties());
Optional类会把真正需要的实例,如上面的object给包装成一个Optional对象,Optional对象的value才是实际需要的对象实例,即object。使用Optional进行判空处理写法上要简洁些。
2、创建 Optional 对象
创建Optional对象一般使用Optional类提供的静态方法。
2.1 Optional.empty()
可以使用静态方法 empty()
创建一个空的 Optional 对象:
Optional<Object> optional = Optional.empty();
System.out.println(optional);
打印结果:
Optional.empty
2.2 Optional.of()
可以使用静态方法 of()
创建一个非空的 Optional 对象:
Optional<String> optional = Optional.of("Jerry");
System.out.println(optional);
打印结果:
Optional[Jerry]
注意:Optional.of()里一定要传入参数,否则会抛异常。
2.3 Optional.ofNullablle
可以使用静态方法 ofNullable()
创建一个即可空又可非空的 Optional 对象:
Optional<String> optional = Optional.ofNullable(null);
System.out.println(optional);
String name = "Jerry";
Optional<String> optional1 = Optional.ofNullable(name);
System.out.println(optional1);
打印结果:
Optional.empty
Optional[Jerry]
其实Optional.ofNullable底层也是调用的Optional.of和Optional.empty(),源码如下:
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
3、获取Optional对象
3.1 get
最直观的获取Optional对象应该是get方法,但其实不然,因为假如 Optional 对象的值为 null,该方法会抛出 NoSuchElementException 异常,这完全与我们使用 Optional 类的初衷相悖。
举例:
public class OptionalDemo {
public static void main(String[] args) {
Optional<String> optional = Optional.ofNullable(null);
String name = optional.get();
System.out.println(name);
}
}
结果:
因此不推荐使用get方法获取optional对象的value。
3.2 orElse
如果Optional
实例有值则将其返回,否则返回orElse
方法传入的参数值。
public T orElse(T other)
举例:
public class OptionalDemo {
public static void main(String[] args) {
// 输出"null时的默认值"
Optional<String> optional = Optional.ofNullable(null);
// 输出Jerry
// Optional<String> optional = Optional.ofNullable("Jerry");
String name = optional.orElse("null时的默认值");
System.out.println(name);
}
}
3.3 orElseGet
orElseGet
与orElse
方法类似,区别在于得到的默认值。orElse
方法将传入的泛型值作为默认值,orElseGet
方法可以接受Supplier
的实现用来生成默认值。
举例:
public class OptionalDemo {
public static void main(String[] args) {
// 输出"null value"
Optional<String> optional = Optional.ofNullable(null);
// 输出Jerry
// Optional<String> optional = Optional.ofNullable("Jerry");
String name = optional.orElseGet(() -> "null value");
System.out.println(name);
}
}
orElse
和orElseGet
的区别:
- 入参不同,orElse接收一个值,orElseGet接收一个lambda表达式,由这个lambda表达式定义如何处理null值;
- 当Optional对象的value不为null时,orElse(getValue())里的getValue()方法依然会执行,但是不会将执行结果返回,orElseGet(() -> {})里则压根不会执行lambda表达式里的代码(详细例子见参考链接1),因此从这一点看orElseGet()方法的效率更高。
综上,获取Optional对象推荐使用orElseGet()方法。
3.4 orElseThrow
如果Optional对象不为null,直接返回value,否则抛出exceptionSupplier的异常。
源码如下:
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
举例:
public class OptionalDemo {
public static void main(String[] args) {
// Optional<String> optional = Optional.ofNullable("jerry");
Optional<String> optional = Optional.ofNullable(null);
try {
String name = optional.orElseThrow(DemoException::new);
System.out.println(name);
} catch (DemoException e) {
e.printStackTrace();
}
}
}
package com.Jerry.optional;
public class DemoException extends Throwable{
public DemoException() {
super();
}
public DemoException(String msg) {
super(msg);
}
@Override
public String getMessage() {
return "Here is message";
}
}
4、非空表达式ifPresent()
ifPresent()方法定义了当Optional对象的value不为null时该怎么继续处理,接收一个Consumer接口,如下:
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
举例:
public class OptionalDemo {
public static void main(String[] args) {
Optional<String> optional = Optional.ofNullable("jerry");
optional.ifPresent(value -> System.out.println(value.length()));
}
}
ifPresent()方法这种链式的写法很简洁舒服,注意lambda表达式里直接是value -> {}。
5、Optional的filter和map
Optional的filter和map方法是偏附加功能的方法,在Stream里也能看到这两个方法,是在创建和获取Optional对象这些基本方法外额外提供的拓展方法。
5.1 filter
filter方法接收一个表达式,如果不满足表达式,则返回一个Optional.empty(),满足表达式则返回过滤后的Optional对象。
源码:
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
举例:Optional对象的value字符串长度大于4的才能返回:
public class OptionalDemo {
public static void main(String[] args) {
// 返回null value
// Optional<String> optional = Optional.ofNullable("Tom");
// 返回Jerry
Optional<String> optional = Optional.ofNullable("Jerry");
// 过滤出value字符串长度大于4的返回
String name = optional.filter(value -> value.length() > 4).orElse("null value");
System.out.println(name);
}
}
当filter里的lambda表达式判断规则有多个时,可以写成这种链式的代码,优雅:
Predicate<String> len6 = pwd -> pwd.length() > 6;
Predicate<String> len10 = pwd -> pwd.length() < 10;
password = "1234567";
opt = Optional.ofNullable(password);
boolean result = opt.filter(len6.and(len10)).isPresent();
System.out.println(result);
5.2 map
map方法可以按照一定的规则将原有 Optional 对象映射为一个新的 Optional 对象,源码如下:
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));
}
}
如果Optional对象的value为null直接返回Optional.empty(),不为null返回映射后的Optional对象。
举例:
public class OptionalDemo {
public static void main(String[] args) {
// 打印结果为0
// Optional<String> optional = Optional.ofNullable(null);
// 映射为value字符串的长度的2倍构成的Optional对象
//打印结果为10
Optional<String> optional = Optional.ofNullable("Jerry");
Optional<Integer> optionalInteger = optional.map(value -> value.length() * 2);
System.out.println(optionalInteger.orElse(0));
}
}
6、使用Optional的注意点
- 当返回结果可能为null时使用Optional;
- 对public方法中的每个入参都进行校验,这种防御型编程虽然可以保护代码,什么都判空,不仅影响效率,还让代码变得不优雅,现实里写代码时还是在容易出现NPE的地方进行判空,需要进行识别;
- 避免使用Optional.get(),推荐使用避免使用Optional.orElseGet();
- 不要将null传给Optional;
- lambda表达式尽量写成单行,如果逻辑比较复杂,封装在方法里,lambda表达式直接调用方法,这不仅是Optional应遵循的规则,在stream等函数式编程里都应遵循这种规范,这样使代码简洁易读。
为什么有人说Optional影响性能?什么场景下会影响性能?
参考:
建议收藏,史上最佳 Java Optional 指南,没有之一
Java Optional 的 orElse() 和 orElseGet() 的区别
浅析Java Optional使用的最佳实践
在Java中,是不是所有的public方法的参数都需要判空处理?