NullPointerException
——空指针异常是程序中常见异常之一,也是导致程序运行失败的常见异常。以前,为了防止出现null
,我们常在代码中使用if…else…
做防御性检查,后来Guava为了解决上述方法造成的代码污染引入了Optional
类。
Java8借鉴Guava的Optional
也加入了同名的Optional
类,Optional
类提供了很多实用的方法,借此可以避免显示的空指针判断,从而避免NullPointerException
。
常见方法
下面逐一讲解Optional
类提供的方法。
of方法
public static Optional of(T value) {
return new Optional<>(value);
}
Optional
中的构造方法都是private
的,外部无法使用new
方式创建Optional
对象,但Optional
类提供三个方法用来获取Optional
的实例对象。of方法是其中之一,用来创建一个包装对象值非空的Optional
实例对象,若包装对象值为空则抛出NullPointerException
异常。
// user若为null则会抛出NullPointerException异常
Optional optional_user = Optional.of(user);
ofNullable方法
public static Optional ofNullable(T value) {
return value == null ? empty() : of(value);
}
获取Optional
实例对象的方法之一,返回一个允许包装对象值为空的Optional
实例对象。ofNullable
与of
方法的区别就在是否允许包装对象为空。
// 可以为空,且optional_user1 == optional_user2为true
Optional optional_user1 = Optional.ofNullable(null);
Optional optional_user2 = Optional.ofNullable(null);
empty方法
public static Optional empty() {
@SuppressWarnings("unchecked")
Optional t = (Optional) EMPTY;
return t;
}
获取Optional
实例对象的方法之一,创建一个包装对象值为空的Optional
实例对象。
// 返回包装对象值为空的Optional实例对象
Optional optional_user = Optional.empty();
isPresent方法
public boolean isPresent() {
return value != null;
}
判断包装对象值是否为空。
// 这里的user不为null
Optional optional_user1 = Optional.ofNullable(user);
Optional optional_user2 = Optional.ofNullable(null);
// 返回true
optional_user1.isPresent();
// 返回false
optional_user2.isPresent();
ifPresent方法
public void ifPresent(Consumer super T> consumer) {
if (value != null)
consumer.accept(value);
}
判断包装对象值是否为空,如果包装对象的值为空则调用Consumer
对象的accept()
方法,不为空则不调用。
Optional.ofNullable(user).ifPresent(() -> {
// 包装对象值为空执行的代码
……
});
get方法
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
返回包装对象的值,若为空则抛出NoSuchElementException
异常。
orElse方法
public T orElse(T other) {
return value != null ? value : other;
}
若包装对象不是null
则返回包装对象的实际值,若为null
则返回指定值,即返回传入的参数。
Optional.ofNullable(user).orElse(new User("zhangsan", 20));
orElseGet方法
public T orElseGet(Supplier extends T> other) {
return value != null ? value : other.get();
}
作用同orElse
方法,只不过orElseGet
的入参是Supplier
对象,当Optional
包装对象为null
时返回指定值,返回值由Supplier
的get
方法产生。
// name是一个String对象
Optional.ofNullable(name).orElseGet(() -> "zhangsan");
orElseThrow方法
public T orElseThrow(Supplier extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
作用同orElseGet
类似,当包装对象不为空时返回包装对象的值,为空时返回一个Throwable
异常。
filter方法
public Optional filter(Predicate super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
过滤Optional
的包装对象,当包装对象满足Predicate
的条件时返回该Optional
实例,否则返回包装对象为null
的Optional
实例。
// 过滤name为zhangsan的User,当不存在时打印提示信息
Optional.ofNullable(user)
.filter(u -> u.getName.equals("zhangsan"))
.ifPresent(() -> System.out.println("There is no zhangsan"));
map方法
public Optional map(Function super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
}
map
方法对包装对象进行函数运算(即调用Function
的apply
方法)后返回一个新的Optional
实例对象,新Optional
实例的包装对象可以时任意类型(不一定与原包装对象的类型保持一致)。
如果包装对象为null
则依然返回一个包装对象为null
的Optional
实例,map可以无限级调用。
// 返回一个Optional
Optional.of(user).map(u -> u.getName());
flatMap方法
public Optional flatMap(Function super T, Optional> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
功能同map
方法,二者的区别在mapper
的apply
方法的返回值类型不同,map
方法中的mapper
返回值可以是任意类型,而flatMap
中的mapper
的返回值是Optional
类型,map
方法会自动封装返回一个Optional
。
// 返回一个Optional,作用同map的例子
Optional.of(user).flatMap(u -> Optional.of(u.getName()));
解决NPE
在由Optional
之前,我们会使用if…else…
做防御性检查,以防止出现空指针异常。
public String getUserName(User user) {
if(user == null) {
return "unknown";
}
return user.getName();
}
有了Optional
之后,我们可以这样做了……
public String getUserName(User user) {
Optional optional_user = Optional.ofNullable(user);
if(!optional_user.isPresent()) {
return "unknown";
}
return user.getName();
}
啊……这……
显然这两种方法并无本质区别,依然都有防御性检查,依然都不够简洁。只不过第二种方法利用isPresent
方法替换了显示的null
判断。
正确的姿势。
public String getUserName(User user) {
// 善用map方法
return Optional.ofNullable(user)
.map(u -> u.getName())
.orElse("unkonwn");
}
这个例子并不能完全展示Optional
的实力,再看下面的示例。
/**
* 获取领导姓名,不使用Optional
* */
public String getBossName(User user) {
if(user != null) {
Company company = user.getCompany();
if(company != null) {
Boss boss = company.getBoss();
if(boss != null) {
return boss.getName();
}
}
}
return "unkonwn";
}
优雅的姿势。
/**
* 获取领导姓名,使用Optional
* */
public String getBossName(User user) {
return Optional.ofNullable(user)
.map(u -> u.getCompany())
.map(c -> c.getBoss())
.map(b -> b.getName())
.orElse("unkonwn");
}
善用Optional
会使我们的代码无比的优雅和简洁。