Java8引入了全新的Optional类,主要用来处理空指针异常(NullPointerException)。从本质上说该类属于包含可选值的封装类(wrapper class),因此它既可以包含对象也可以仅仅为空。
举例说明:
在 Java 8 之前,凡涉及到访问对象方法或者对象属性的操作,无论数量多寡,都可能导致空指针异常:
String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();
为确保上面实例不出现空指针异常,需对每一个值进行显式的null检查
if (user != null) {
Address address = user.getAddress();
if (address != null) {
Country country = address.getCountry();
if (country != null) {
String isocode = country.getIsocode();
if (isocode != null) {
isocode = isocode.toUpperCase();
}
}
}
}
但是很多时候会忘记null的检查,即使没有忘记写出上面的代码也将很难维护
为了解决上述问题,JDK在Java8的时候加入了Optional,Optional的javadoc介绍如下
A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.
这是一个可以包含或者不包含非 null 值的容器。如果值存在则 isPresent()方法会返回 true,调用 get() 方法会返回该对象
构造Optional
JDK提供了三个静态方法来构造一个Optional
Optional.of(T value)
该方法通过一个非null的value来构造一个Optional,返回的Optional包含了value这个值,对于该方法,传入的参数一定不能为null,否则会抛出NullPointerException
Optional.ofNullable(T value)
该方法和of的区别在于,传入的参数可以为null,与之前Optional javadoc提到的不为null有冲突,原因可以参考ofNullable方法的源码
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
该方法会对传入的参数进行null判断,如果为null,实际上返回的是Optional.empty
Optional.empty()
该方法用来构造一个空的Optional,底层代码实现如下
/**
* Common instance for {@code empty()}.
*/
private static final Optional<?> EMPTY = new Optional<>();
/**
* If non-null, the value; if null, indicates no value is present
*/
private final T value;
/**
* Constructs an empty instance.
*
* @implNote Generally only one empty instance, {@link Optional#EMPTY},
* should exist per VM.
*/
private Optional() {
this.value = null;
}
方法使用总结
前面JavaDoc提到,Optional的isPresent()用来判断是否包含值,get()方法用来获取Optional包含的值,需要注意在Optional.empty上调用get()方法将抛出NoSuchElementException异常
举例如下,完整代码在文章底部
Optional<User> user = Optional.ofNullable(getUserById(id));
if (user.isPresent()) {
String username = user.get().getUsername();
System.out.println("Username is: " + username); // 使用 username
}
和之前使用null的判断没啥区别,还需要使用Optional封装value,增加了代码里,实际上这不是使用Optional的正确姿势,下面将详细介绍Optional各个方法的使用
ifPresent
底层源码
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
上面获取用户名的例子可以重构成如下代码
Optional<User> user = Optional.ofNullable(getUserById(userId));
user.ifPresent(u -> System.out.println(u.getUsername()));
orElse
底层源码
public T orElse(T other) {
return value != null ? value : other;
}
使用举例
User user1 = Optional
.ofNullable(getUserById(nullUserId))
.orElse(new User("Unknown", "Unknown", 99));
System.out.println(user1.toString());
orElseGet
orElseGet与orElse的区别在于,orElseGet方法传入的参数为一个Supplier接口的实现,当Optional有值的时候,返回值,没有值的时候,返回该Supplier的值
底层源码
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
使用举例
User user2 = Optional
.ofNullable(getUserById(userId))
.orElseGet(() -> new User("Unknown", "unknown", 88));
System.out.println(user2.toString());
orElseThrow
orElseThrow与orElse方法的区别,orElseThrow方法当Optional中有值的时候返回值,没有值的时候抛出异常,抛出的异常由传入的exceptionSupplier提供
底层源码
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
使用举例
try {
User user3 = Optional
.ofNullable(getUserById(nullUserId))
.orElseThrow(() -> new Exception("cannot found user of " + nullUserId));
} catch (Exception e) {
e.printStackTrace();
}
map
如果当前Optional为Optional.empty,则依旧返回Optional.empty;否则返回一个新的Optional,该Optional包含的是:函数mapper在以value作为输入时的输出值,可以多次使用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));
}
}
使用举例
String userName = Optional.ofNullable(getUserById(nullUserId))
.map(u -> u.getUsername())
.map(name -> name.toLowerCase())
.orElse("Unknown");
System.out.println(userName);
flatMap
flatMap与map方法的区别在于,map方法参数中函数mapper输出的是值,然后map方法会使用Optional.ofNullable将其包装为Optional,而flatMap要求参数中的函数mapper输出的就是Optional
底层源码
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
使用举例
Optional<String> userName1 = Optional.ofNullable(getUserById(userId))
.flatMap(u -> Optional.of(u.getUsername()))
.flatMap(name -> Optional.of(name.toLowerCase()));
System.out.println(userName1);
filter
filter方法接收一个Predicate来对Optional中包含的值进行过滤,如果包含的值满足条件,则还是返回这个Optional,否则返回Optional.empty
底层源码
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
使用举例
Optional<String> userName2 = Optional.ofNullable(getUserById(userId))
.filter(u -> u.getUsername() != "Jack")
.map(u -> u.getUsername());
System.out.println(userName2);
完整代码
public static void main(String[] args) {
int nullUserId = 0;
int userId = 666;
Optional<User> user = Optional.ofNullable(getUserById(userId));
user.ifPresent(u -> System.out.println(u.getUsername()));
User tmpUser = new User();
user.ifPresent(u -> tmpUser.setUsername(u.getUsername()));
System.out.println(tmpUser.toString());
user.ifPresent(u -> {int age = u.getAge();
System.out.println(age);});
User user1 = Optional
.ofNullable(getUserById(nullUserId))
.orElse(new User("Unknown", "Unknown", 99));
System.out.println(user1.toString());
User user2 = Optional
.ofNullable(getUserById(userId))
.orElseGet(() -> new User("Unknown", "unknown", 88));
System.out.println(user2.toString());
try {
User user3 = Optional
.ofNullable(getUserById(nullUserId))
.orElseThrow(() -> new Exception("cannot found user of " + nullUserId));
} catch (Exception e) {
e.printStackTrace();
}
String userName = Optional.ofNullable(getUserById(nullUserId))
.map(u -> u.getUsername())
.map(name -> name.toLowerCase())
.orElse("Unknown");
System.out.println(userName);
Optional<String> userName1 = Optional.ofNullable(getUserById(userId))
.flatMap(u -> Optional.of(u.getUsername()))
.flatMap(name -> Optional.of(name.toLowerCase()));
System.out.println(userName1);
Optional<String> userName2 = Optional.ofNullable(getUserById(userId))
.filter(u -> u.getUsername() != "Jack")
.map(u -> u.getUsername());
System.out.println(userName2);
}
private static User getUserById(int userId) {
if (666 == userId) {
return new User("Jack", "Beijing", 18);
} else {
return null;
}
}
网友评论