Java8新特性Optional类
这里写自定义目录标题
Java8 Optional 类
前言
在 Java 8 之前,任何访问对象方法或属性的调用都可能导致 NullPointerException;
在这个小示例中,如果我们需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查;
if (user != null) {
Address address = user.getAddress();
if (address != null) {
Country country = address.getCountry();
if (country != null) {
String code = country.getCountryCode();
if (isocode != null) {
code = code.toUpperCase();
}
}
}
}
这很容易就变得冗长,难以维护。
为了简化这个过程,我们来看看用 Optional 类是怎么做的。从创建和验证实例,到使用其不同的方法,并与其它返回相同类型的方法相结合,下面是见证 Optional 奇迹的时刻。
创建 Optional 实例
重申一下,这个类型的对象可能包含值,也可能为空。你可以使用同名方法创建一个空的 Optional。
@Test
public void createEmptyOptionalTest() {
// 使用同名方法创建一个空的 Optional
Optional<User> opt = Optional.empty();
// 报错 NoSuchElementException
opt.get();
}
尝试访问 emptyOpt 变量的值会导致 NoSuchElementException。
可以使用 of() 和 ofNullable() 方法创建包含值的 Optional。两个方法的不同之处在于如果你把 null 值作为参数传递进去,of() 方法会抛出 NullPointerException:
@Test
public void createOfEmptyOptionalTest() {
User user = null;
// 把 null 值作为参数传递进去,of() 方法会抛出 NullPointerException
Optional<User> opt = Optional.of(user);
// 使用 ofNullable() 方法,对象即可能是 null 也可能是非 null
Optional<User> opt1 = Optional.ofNullable(user);
}
把 null 值作为参数传递进去,使用 of() 方法会抛出 NullPointerException,而使用 ofNullable() 方法,则返回一个空的 Optional 类。
因此,应该明确对象不为 null 的时候使用 of(),如果对象即可能是 null 也可能是非 null,你就应该使用 ofNullable() 方法。
Optional API常用接口的用法
get()方法
Instant标识某个时间(类似Date),精确到纳秒(不像Date到毫秒),因为精确到纳秒,所以用一位Long类型是不够的的,所以实际是有两个Long字段组成,第一部分保存的是子1970年1月1日开始到现在的秒数,滴而部分保存的是纳秒数;
@Test
public void getTest() {
String name = "hhh";
Optional<String> opt = Optional.ofNullable(name);
System.out.println(opt.get());
}
·控制台输出
hhh
可以看到,get()方法主要用于返回包装对象的实际值,但是如果包装对象值为null,会抛出NoSuchElementException异常。
isPresent()方法
@Test
public void isPresentTest() {
User user = new User("[email protected]", "1234");
Optional<User> opt = Optional.ofNullable(user);
System.out.println(opt.isPresent());
if (opt.isPresent()) {
System.out.println(opt.get());
}
}
·控制台输出
true
User(code=hhh@email.com, name=1234, address=Optional.empty)
isPresent()方法用于判断包装对象的值是否非空,但是这种用法不但没有减少null的防御性检查,而且增加了Optional包装的过程
isPresent()方法
@Test
public void ifPresentTest() {
User user = new User("[email protected]", "1234");
Optional<User> opt = Optional.ofNullable(user);
opt.ifPresent(u -> System.out.println(u));
}
·控制台输出
User(code=hhh@email.com, name=1234, address=Optional.empty)
ifPresent() 方法除了执行检查,还接受一个Consumer(消费者) 参数,如果对象不是空的,就对执行传入的 Lambda 表达式
filter()方法
@Test
public void filterTest() {
User user = new User("[email protected]", "1234");
Optional<User> result = Optional.ofNullable(user).filter(u -> u.getCode() != null && u.getCode().contains("@"));
System.out.println(result);
}
·控制台输出
Optional[User(code=ccc@email.com, name=1234, address=Optional.empty)]
filter() 接受一个 Predicate 参数,返回测试结果为 true 的值。如果测试结果为 false,会返回一个空的 Optional
map()方法
@Test
public void mapTest() {
User user = new User("[email protected]", "1234");
Optional<String> email = Optional.ofNullable(user).map(u -> u.getCode());
System.out.println(email);
}
·控制台输出
Optional[ccc@email.com]
map()方法的参数为Function(函数式接口)对象,map()方法将Optional中的包装对象用Function函数进行运算,并包装成新的Optional对象
flatMap()方法
public void flatMapTest() {
User user = new User("[email protected]", "1234");
Optional<String> email = Optional.ofNullable(user).flatMap(u -> u.getCode());
System.out.println(email);
}
·控制台输出
Optional[anna@gmail.com]
跟map()方法不同的是,入参Function函数的返回值类型为Optional类型,而不是U类型,这样flatMap()能将一个二维的Optional对象映射成一个一维的对象
orElse()方法
private User createNewUser() {
System.out.println("Create New User");
return new User("[email protected]", "1234");
}
@Test
public void orElseTest() {
User user = new User("[email protected]", "1234");
User result = Optional.ofNullable(user).orElse(createNewUser());
System.out.println(result);
User user1 = null;
User result1 = Optional.ofNullable(user1).orElse(createNewUser());
System.out.println(result1);
}
·控制台输出
Create New User
User(code=hhh@email.com, name=1234, address=Optional.empty)
Create New User
User(code=hhh@email.com, name=1234, address=Optional.empty)
orElse()方法,不论对象User是否为空值都会调用createNewUser()方法创建User对象
orElseGet()方法
private User createNewUser() {
System.out.println("Create New User");
return new User("[email protected]", "1234");
}
@Test
public void orElseGetTest() {
User user = new User("[email protected]", "1234");
User result = Optional.ofNullable(user).orElseGet(createNewUser());
System.out.println(result);
User user1 = null;
User result1 = Optional.ofNullable(user1).orElseGet(createNewUser());
System.out.println(result1);
}
·控制台输出
User(code=hhh@email.com, name=1234, address=Optional.empty)
Create New User
User(code=hhh@email.com, name=1234, address=Optional.empty)
orElseGet()方法与orElse()方法不同,User对象非空值时,方法会返回对应的对象,对象为空值时,才调用createNewUser()方法创建User对象
orElseThrow()方法
@Test
public void orElseThrowTest() {
User user = new User("[email protected]", "1234");
User result = Optional.ofNullable(user).orElseThrow( () -> new IllegalArgumentException());
System.out.println(result);
}
·控制台输出
User(code=hhh@email.com, name=1234, address=Optional.empty)
orElseThrow()的Supplier对象必须返回一个Throwable异常,并在orElseThrow()中将异常抛出,orElseThrow()方法适用于包装对象值为空时需要抛出特定异常的场景
Optional 类的链式方法
为了更充分的使用 Optional,可以链接组合其大部分方法,因为它们都返回相同类似的对象
首先,重构类,使其 getter 方法返回 Optional 引用
public class User {
private String code;
private String name;
private Address address;
public Optional<Address> getAddress(){
return Optional.ofNullable(address);
}
}
public class Address {
private Country country;
public Optional<Country> getCountry(){
return Optional.ofNullable(country);
}
}
现在可以删除 null 检查,替换为 Optional 的方法
public void chainingTest() {
User user = new User("[email protected]", "1234");
String result = Optional.ofNullable(user)
.flatMap(u -> u.getAddress())
.flatMap(a -> a.getCountry())
.map(c -> c.getCountryCode())
.orElse("default");
System.out.println(result);
}
·控制台输出
default
上面的代码可以通过方法引用进一步缩减
public void chainingTest() {
User user = new User("[email protected]", "1234");
String result = Optional.ofNullable(user)
.flatMap(User::getAddress())
.flatMap(Address::getCountry())
.map(Country::getCountryCode())
.orElse("default");
System.out.println(result);
}
·控制台输出
default
结果现在的代码看起来比之前采用条件分支的冗长代码简洁多了
注意事项
使用Optional开发时要注意正确使用Optional的“姿势”,特别注意不要使用3.2节提到的错误示范,谨慎使用isPresent()和get()方法,尽量多使用map()、filter()、orElse()等方法来发挥Optional的作用
上一篇: Vue定时器问题解析
下一篇: vue中关于变量的定义解析
推荐阅读
-
Java8中新特性Optional、接口中默认方法和静态方法详解
-
Java8新特性之JavaFX 8_动力节点Java学院整理
-
Java8新特性之Base64详解_动力节点Java学院整理
-
Java8新特性之泛型的目标类型推断_动力节点Java学院整理
-
Java8新特性之StampedLock_动力节点Java学院整理
-
Java8新特性之lambda的作用_动力节点Java学院整理
-
Java8新特性之类型注解_动力节点Java学院整理
-
Java8新特性之再见Permgen_动力节点Java学院整理
-
Java8新特性之精简的JRE详解_动力节点Java学院整理
-
Java8新特性之深入解析日期和时间_动力节点Java学院整理