Java8中Optional详解
文章目录
Optional的使用
1.前言
public class User {
private Address address;
public Address getAddress() {
return address;
}
}
public class Address {
private Country country;
public Country getCountry() {
return country;
}
}
public class Country {
private String cityName;
public String getCityName() {
return cityName;
}
}
在Java 8之前,任何访问对象方法或属性的调用都可能导致NullPointerException:
String cityName = user.getAddress().getCountry().getCityName();
如果需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查:
if (user != null) {
Address address = user.getAddress();
if (address != null) {
Country country = address.getCountry();
if (country != null) {
String cityName = country.getCityName();
}
}
}
这很容易就变得冗长,难以维护。为了简化这个过程,使用Optional类进行操作。
2.Optional介绍
可能包含也可能不包含非null值的容器对象。如果一个值存在,
isPresent()
将返回true并且get()
将返回其值。
提供了依赖于是否存在包含值的其他方法,例如orElse()
(如果值不存在则返回默认值)和ifPresent()
(如果值存在则执行代码块)。
这是一个基于值的类;在Optional实例上使用标识敏感操作(包括引用相等(==),标识哈希码或同步)可能会产生不可预知的结果,应该避免使用。
序号 | 方法 & 描述 |
---|---|
1 | **static Optional empty()**返回空的 Optional 实例。 |
2 | **boolean equals(Object obj)**判断其他对象是否等于 Optional。 |
3 | **Optional filter(Predicate<? super predicate)**如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional。 |
4 | ** Optional flatMap(Function<? super T,Optional> mapper)**如果值存在,返回基于Optional包含的映射方法的值,否则返回一个空的Optional |
5 | **T get()**如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException |
6 | **int hashCode()**返回存在值的哈希码,如果值不存在 返回 0。 |
7 | **void ifPresent(Consumer<? super T> consumer)**如果值存在则使用该值调用 consumer , 否则不做任何事情。 |
8 | **boolean isPresent()**如果值存在则方法会返回true,否则返回 false。 |
9 | **Optional map(Function<? super T,? extends U> mapper)**如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional。 |
10 | **static Optional of(T value)**返回一个指定非null值的Optional。 |
11 | **static Optional ofNullable(T value)**如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。 |
12 | **T orElse(T other)**如果存在该值,返回值, 否则返回 other。 |
13 | **T orElseGet(Supplier<? extends T> other)**如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果。 |
14 | ** T orElseThrow(Supplier<? extends X> exceptionSupplier)**如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常 |
15 | **String toString()**返回一个Optional的非空字符串,用来调试 |
修饰符和类型 | 方法和描述 |
---|---|
static <T> Optional<T> |
empty() 返回一个空Optional 实例。 |
boolean |
equals(Object obj) 指示某个其他对象是否“等于”此Optional。 |
Optional<T> |
filter(Predicate<? super T> predicate) 如果存在值,并且值与给定谓词匹配,则返回Optional 描述该值的值,否则返回空值Optional 。 |
<U> Optional<U> |
flatMap(Function<? super T,Optional<U>> mapper) 如果存在值,则将提供的Optional -bearing mapping函数应用于该值,返回该结果,否则返回空 Optional 。 |
T |
get() 如果此值中存在Optional 值,则返回该值,否则抛出NoSuchElementException 。 |
int |
hashCode() 返回当前值的哈希码值(如果有),如果不存在值,则返回0(零)。 |
void |
ifPresent(Consumer<? super T> consumer) 如果存在值,则使用值调用指定的使用者,否则不执行任何操作。 |
boolean |
isPresent()``true 如果存在值则返回,否则返回false 。 |
<U> Optional<U> |
map(Function<? super T,? extends U> mapper) 如果存在值,则将提供的映射函数应用于该值,如果结果为非null,则返回Optional 描述结果的值。 |
static <T> Optional<T> |
of(T value) 返回Optional 具有指定的当前非null值的a。 |
static <T> Optional<T> |
ofNullable(T value) 返回Optional 描述指定值的值,如果为非null,否则返回空值Optional 。 |
T |
orElse(T other) 如果存在则返回值,否则返回other 。 |
T |
orElseGet(Supplier<? extends T> other) 返回值(如果存在),否则调用other 并返回该调用的结果。 |
<X extends Throwable>T |
orElseThrow(Supplier<? extends X> exceptionSupplier) 返回包含的值(如果存在),否则抛出由提供的供应商创建的异常。 |
String |
toString() 返回此Optional的非空字符串表示形式,适用于调试。 |
3.创建Optional
3.1empty
返回一个空Optional实例。此Optional没有值。
注意: 尽管这样做很有诱惑力,但是要避免测试对象是否为空通过将==
与Option.empty()
返回的实例进行比较。不能保证它是单例的。使用isPresent()
替代。
即:通过isPresent()
判断对象是否为空。
Optional<User> empty = Optional.empty();
3.2of
返回具有指定的当前非空值的Optional。如果值为null则抛出NullPointerException
。
Optional<User> userOptional = Optional.of(null);
3.3ofNullable
如果为非null,返回描述指定值的Optional,否则返回空Optional。
如果对象可能是null也可能是非null,应该使用ofNullable()。
Optional<User> userOptional = Optional.ofNullable(null);
4.获取Optional的值
4.1get()
如果此Optional中存在值,则返回该值,否则抛出NoSuchElementException
。
Optional<User> userOptional = Optional.ofNullable(null);
if (userOptional.isPresent()) {
User user = userOptional.get();
}
5.检查Optional是否有值
5.1isPresent()
如果存在值,则返回true,否则返回false。
Optional<User> userOptional = Optional.ofNullable(null);
boolean isPresent = userOptional.isPresent();
5.2ifPresent()
如果存在值,则使用该值调用指定的消费者,否则不执行任何操作。
Optional<User> userOptional = Optional.ofNullable(null);
userOptional.ifPresent(user -> System.out.println(user.getAddress()));
6.filter
如果存在值,并且值与给定谓词匹配,则返回描述该值的Optional
,否则返回空Optional
。
Optional<User> userOptional = Optional.ofNullable(null);
Optional<User> result = userOptional.filter(user -> user.getAddress() != null);
7.map
如果存在值,则将提供的映射函数应用于该值,如果结果为非null,则返回描述结果的Optional
。否则返回空Optional
。
注意: 此方法支持对optional值进行后处理,而无需显式检查返回状态。例如,以下代码遍历文件名流,选择一个尚未处理的文件名,然后打开该文件,返回 Optional<FileInputStream>
:
Optional<FileInputStream> fis = names.stream()
.filter(name -> !isProcessedYet(name))
.findFirst()
.map(name -> new FileInputStream(name));
在这里,findFirst
返回一个Optional<String>
,然后map
返回一个所需文件的Optional<FileInputStream>
(如果存在)。
Optional<User> userOptional = Optional.ofNullable(null);
Optional<Address> address = userOptional.map(User::getAddress);
Optional<Country> country = address.map(Address::getCountry);
使用map进行链式调用
String result = Optional.ofNullable(new User())
.map(User::getAddress)
.map(Address::getCountry)
.map(Country::getCityName)
.orElse("上海");
System.out.println(result);
// 上海
如果属性值以Optional封装
public class User {
private Optional<Address> address;
public Optional<Address> getAddress() {
return address;
}
}
public class Address {
private Optional<Country> country;
public Optional<Country> getCountry() {
return country;
}
}
public class Country {
private String cityName;
public String getCityName() {
return cityName;
}
}
Optional<User> userOptional = Optional.ofNullable(null);
Optional<Optional<Address>> address = userOptional.map(User::getAddress);
可以看出,map返回的是包装原结果类型的Optional类型。
8.flatMap
如果存在值,则将提供的Optional-bearing
映射函数应用于该值,返回该结果,否则返回空Optional
。这个方法类似于map(Function)
,但提供的映射器是一个结果已经是Optional
的映射器,如果被调用,flatMap
不会附加一个Optional
。
注意:和map的区别是不会附加一个Optional
Optional<String> stringOptional = Optional.ofNullable("hello")
.flatMap(a -> Optional.ofNullable(a + " world!"));
System.out.println(stringOptional.get());
// hello world!
如果属性值以Optional封装(参见map)
User user = null;
String cityName = Optional.ofNullable(user).flatMap(User::getAddress)
.flatMap(Address::getCountry)
.map(Country::getCityName).orElse("上海");
System.out.println(cityName);
// 上海
值得注意的是,一些博客并没有很详细的说明flatMap和map到底有什么区别,上面的例子如果改成这样
User user = new User();
String cityName = Optional.ofNullable(user)
.flatMap(User::getAddress)
.flatMap(Address::getCountry)
.map(Country::getCityName).orElse("上海");
System.out.println(cityName);
会报空指针异常Exception in thread "main" java.lang.NullPointerException
对比map和flatMap的源码,map返回的是Optional.ofNullable
包装的U
,而flatMap返回的是Objects.requireNonNull
包装的Optional<U>
,如果mapper.apply(value)
的执行结果为null,那么flatMap就会抛出NullPointerException
。
上面的例子也验证了这一点,而且使用flatMap还需要改变原有实体类属性的类型,不符合编程习惯不说,并不能解决链式调用中某一环节出现NullPointerException的情况。
所以,推荐使用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));
}
}
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));
}
}
9.返回默认值
9.1orElse
如果存在则返回值,否则返回other
。
String s = null;
String str = Optional.ofNullable(s).orElse("default");
System.out.println(str);
// default
9.2orElseGet
如果存在则返回值,否则调用other
并返回该调用的结果。
注意: 如果值不存在且other
为null则抛出NullPointerException
String s = null;
String s1 = Optional.ofNullable(s).orElseGet(() -> null);
System.out.println(s1);
String s2 = Optional.ofNullable(s).orElseGet(null);
System.out.println(s2);
// null
// Exception in thread "main" java.lang.NullPointerException
9.3orElseThrow
如果存在返回包含的值,否则抛出由提供的供应商创建的异常。
注意: 具有空参数列表的异常构造函数的方法引用可以用作供应商。例如, IllegalStateException::new
String orElseThrow = Optional.ofNullable(s).orElseThrow(IllegalArgumentException::new);
System.out.println(orElseThrow);
这个方法丰富了语义,可以决定抛出什么样的异常。
10.orElse和orElseGet的不同之处
对象为空时两者的行为:
private static User createNewUser() {
System.out.println("Creating New User");
return new User();
}
User user = null;
System.out.println("Using orElse");
User result1 = Optional.ofNullable(user).orElse(createNewUser());
System.out.println("Using orElseGet");
User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
// Using orElse
// Creating New User
// Using orElseGet
// Creating New User
当对象为空而返回默认对象时,行为并无差异。
对象不为空时两者的行为:
User user = new User();
System.out.println("Using orElse");
User result1 = Optional.ofNullable(user).orElse(createNewUser());
System.out.println("Using orElseGet");
User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
// Using orElse
// Creating New User
// Using orElseGet
两个Optional对象都包含非空值,两个方法都会返回对应的非空值。不过,orElse()
方法仍然创建了User对象。与之相反,orElseGet()方法不创建User对象。
private static String getString() {
System.out.println("调用我了");
return "bbb";
}
String s1 = Optional.ofNullable("aaa").orElse(getString());
System.out.println(s1);
// 调用我了
//aaa
在执行较密集的调用时,比如调用Web服务或数据查询,这个差异会对性能产生重大影响。
11.Java9增强
Java 9为Optional类增加了三个方法:or()、ifPresentOrElse()和stream()。
or()方法与orElse()和orElseGet()类似,它们都在对象为空的时候提供了替代情况。or() 的返回值是由Supplier参数产生的另一个Optional对象。
如果对象包含值,则Lambda表达式不会执行:
@Test
public void whenEmptyOptional_thenGetValueFromOr() {
User result = Optional.ofNullable(user)
.or( () -> Optional.of(new User("default","1234"))).get();
assertEquals(result.getEmail(), "default");
}
上面的示例中,如果 user 变量是 null,它会返回一个 Optional,它所包含的 User 对象,其电子邮件为 “default”。
ifPresentOrElse() 方法需要两个参数:一个 Consumer 和一个 Runnable。如果对象包含值,会执行 Consumer 的动作,否则运行 Runnable。
如果你想在有值的时候执行某个动作,或者只是跟踪是否定义了某个值,那么这个方法非常有用:
Optional.ofNullable(user).ifPresentOrElse( u -> logger.info("User is:" + u.getEmail()),
() -> logger.info("User not found"));
最后介绍的是新的 stream() 方法,它通过把实例转换为 Stream 对象,让你从广大的 Stream API 中受益。如果没有值,它会得到空的 Stream;有值的情况下,Stream 则会包含单一值。
我们来看一个把 Optional 处理成 Stream 的例子:
@Test
public void whenGetStream_thenOk() {
User user = new User("[email protected]", "1234");
List<String> emails = Optional.ofNullable(user)
.stream()
.filter(u -> u.getEmail() != null && u.getEmail().contains("@"))
.map( u -> u.getEmail())
.collect(Collectors.toList());
assertTrue(emails.size() == 1);
assertEquals(emails.get(0), user.getEmail());
}
这里对 Stream 的使用带来了其 filter()、map() 和 collect() 接口,以获取 List。
本文参考:
理解、学习与使用 JAVA 中的 OPTIONAL
Optional (Java Platform SE 8 )