根据Oracle文档,Optional是一个容器对象,可以包含也可以不包含非null值。Optional在Java 8中引入,目的是解决 NullPointerExceptions的问题。本质上,Optional是一个包装器类,其中包含对其他对象的引用。在这种情况下,对象只是指向内存位置的指针,并且也可以指向任何内容。从其它角度看,Optional提供一种类型级解决方案来表示可选值而不是空引用。
在Optional之前
在Java 8之前,程序员将返回null而不是Optional。这种方法有一些缺点。一种是没有明确的方法来表示null可能是一个特殊值。相比之下,在API中返回Optional是明确的声明,其中可能没有值。如果我们要确保不会出现空指针异常,则需要对每个引用进行显式的空检查,如下所示,我们都同意这是很多样板。
private void getIsoCode( User user){
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();
}
}
}
}
}
Optional使用
Optional.empty();
Optional<String> empty = Optional.empty();
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
返回一个空的Optional对象,EMPTY定义如下
private static final Optional<?> EMPTY = new Optional<>();
注意,Optional除了构造函数,无法再进行value的set操作,一旦一个Optional对象建立,就不能再改变了。看起来如果是空的empty对象,那么都应该是EMPTY这个示例,那么我们对Optional对象判空能不能使用optional == EMPTY这种形式。如果所有空Option是单例的话那么是可以的,显然Empty的定义方式决定其是一个单例
private Optional() {
this.value = null;
}
value = null的构造也只有创建Empty的地方能够调用
System.out.println(new ArrayList<>().stream().findFirst()==Optional.empty());
输出结果为true
Optional.of()
返回特定的非空值Optional。
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
要求value不能为空
// Creating an optional using of
String name = "java";
Optional<String> opt = Optional.of(name);
静态方法需要一个非null参数;否则,将引发空指针异常。因此,如果我们不知道参数是否为null,那就是我们使用 ofNullable的时候,下面将对此进行介绍。
Optional.ofNullable
返回描述指定值的Optional,如果非空,则返回空值。
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
可见创建的空对象依然是初始化的静态单例Empty
// Possible null value
Optional<String> optional = Optional.ofNullable(name());
private String name(){
String name = "Java";
return (name.length() > 5) ? name : null;
}
ifPresent
public boolean isPresent() {
return value != null;
}
根据value是否为空返回bool值
如果存在值,则返回true;反之,返回false。如果所包含的对象不为null,则返回true,反之返回false。通常在对对象执行任何其他操作之前,先在Optional上调用此方法。
无返回值方法
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
如果存在值,则使用该值调用指定的使用者;否则,什么都不做。
get
如果此Optional中存在值,则返回该值,否则抛出 NoSuchElementException。在这之后,我们想要的是存储在Optional中的值,我们可以通过get()来获取它。但是,当该值为null时,此方法将引发异常。这就需要 orElse() 方法来紧急救援。
//get
Optional<String> optional1 = Optional.of("javaone");
if (optional1.isPresent()){
String value = optional1.get();
}
orElse
public T orElse(T other) {
return value != null ? value : other;
}
该 orElse() 方法用于检索包装在Optional实例内的值。它采用一个充当默认值的参数。该 orElse() 方法返回包装的值(如果存在)及其参数.
orElseGet
返回值(如果存在);否则,调用other并返回该调用的结果。
该orElseGet() 方法类似于 orElse()。但是,如果没有Optional值,则不采用返回值,而是采用供应商功能接口,该接口将被调用并返回调用的值:
//orElseGet
String name = Optional.ofNullable(nullName).orElseGet(() -> "john");
orElseThrow
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
功能类似,如果value,根据exceptionSupplier抛出异常
filter
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
根据传入的的predicate条件,处理value,如果条件满足,返回当前Optional否则,返回空的Optional
List<String> list = Arrays.asList("tom", "jame", "jerry", "hello");
System.out.println(list.stream().findFirst().filter(StringUtil::isNullOrEmpty));
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));
}
}
对value进行转换,如果为空,返回空的Option
List<String> list = Arrays.asList("tom", "jame", "jerry", "hello");
System.out.println(list.stream().findFirst().map(String::length));
flatMap
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));
}
}
与map的区别在于,要求用户自己将转换结果转为Optional对象。
List<String> list = Arrays.asList("tom", "jame", "jerry", "hello");
System.out.println(list.stream().findFirst().flatMap(it->Optional.of(it.length())));
解决了什么问题
Optional的方法是尝试通过增加构建更具表现力的API的可能性来减少Java系统中空指针异常的情况,这些API解释了有时缺少返回值的可能性。
在Optional,api的限制下,我们无法获取到空的value进行直接操作,这这样大大减少了空指针异常的情况
解决不了什么问题
Optional并不意味着是一种避免所有类型的空指针的机制。例如,它仍然必须测试方法和构造函数的强制输入参数。
例如判空并get一个对象后,Optional不具有传递性,当再操作这个对象的属性时,仍然可能报出空指针异常
何时使用
Optional
的预期用途
主要是作为返回类型
。获取此类型的实例后,可以提取该值(如果存在)或提供其他行为(如果不存在)。
Optional类的一个非常有用的用例是将其与流或返回Optional值以构建流畅的API的其他方法结合。请参见下面的代码段
User user = users.stream().findFirst().orElse(new User("default", "1234"));
何时不要使用
a)不要将其用作类中的字段,因为它不可序列化
如果确实需要序列化包含Optional值的对象,则Jackson库提供了将Optionals视为普通对象的支持。这意味着Jackson将空对象视为空,将具有值的对象视为包含该值的字段。可以在jackson-modules-java8项目中找到此功能。
b)不要将其用作构造函数和方法的参数,因为这会导致不必要的复杂代码。