Optional是JDK8提供的一个很简单的容器类,其目的就是把对象包装在容器里面,然后提供对该对象的一些基本操作。整个类加上注释也才300多行。
package java.util;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public final class Optional<T> {
private static final Optional<?> EMPTY = new Optional<>();
private final T value;
private Optional() {
this.value = null;
}
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
public boolean isPresent() {
return value != null;
}
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
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));
}
}
public T orElse(T other) {
return value != null ? value : other;
}
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Optional)) {
return false;
}
Optional<?> other = (Optional<?>) obj;
return Objects.equals(value, other.value);
}
@Override
public String toString() {
return value != null
? String.format("Optional[%s]", value)
: "Optional.empty";
}
}
上述是Optional所有的源码,我只是把其注释去掉。
empty()
是一个Optional中包含了一个对象为null的方法;of(T value)
与ofNullable(T value)
都是创建一个Optional的对象,区别在于前者方法的参数不能为空,如果为空会报npe;后者可以为空,会生成一个为null的Optional对象;(建议用后者)Optional. Optional.ofNullable(obj).orElse(null);
Optional.of(obj).orElse(null); //此处的orElse是没有任何意义的,为空时会直接抛出异常
get()
该方法是为了获取被Optional包装内的对象。 public class OptionalObject{
private OptionalObject optionalObject;
private String name;
public OptionalObject(String name){
System.out.println("init OptionalObject".concat(name));
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
String name = Optional.ofNullable(optionalObject).map(OptionalObject::getName).get(); //此处返回的是optionlObject中的name属性
}
}
ifPresent(Consumer super T> consumer)
判断该Optional中的对象是否为空,该方法通常配合ofNullable使用,如果存在会执行该方法参数的函数表达式;
filter(Predicate super T> predicate)
用于过滤不满足当前判断条件的对象,如果满足条件就正常返回如果不满足就返回null的;(个人感觉此处源码不完美)源码是这样的:
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
如果我写的话:
1.
public Optional<T> filter(Predicate<? super T> predicate) {
if(isPresent()){
Objects.requireNonNull(predicate);
return predicate.test(value) ? this : empty();
}
return this;
}
2.
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
return isPresent() && predicate.test(value) ? this : empty();
}
更改规则:1.尽量避免if else 逻辑;2.可选条件用时校验,必须条件前置校验。此处的check predicate是一个可选的,因为只有在isPresent为true的情况下才会被调用,
因此此处可以在走到对应的逻辑中再check也不迟嘛;当然JDK可能认为此处一定不能为空,所以加上的前置校验。 前置校验加在方法的入口端,也就是执行完该方法你必须要满足该条件,
加在入口处是为了防止该方法快执行完了然后才告诉我校验不通过,运行了一些不必要的代码。
就校验来看,规则一和规则二似乎是冲突的,我认为这是在某种程度上是为了配合规则一对规则二所做出的一些牺牲。
map(Function super T, ? extends U> mapper) 和 flatMap(Function super T, Optional> mapper);
两个方法都是返回Optional的对象,但是对于两者传的函数参数来说是不一样的,前者返回的是一个具体的对象,而后者返回的是一个内置具体对象的Optional对象。 故名思义实际上flatMap的功能就是将对象打平操作,也就是将嵌套的Optional对象提取出来的过程。@Data
public class OptionalDemo {
private String name;
private Optional<String> nameOptional;
public static void main(String[] args) {
OptionalDemo optionalDemo = new OptionalDemo();
String result1 = Optional.ofNullable(optionalDemo).map(OptionalDemo::getName).orElse(null);
//编译报错
String result2 = Optional.ofNullable(optionalDemo).map(OptionalDemo::getNameOptional).orElse(null);
//编译报错
String result3 = Optional.ofNullable(optionalDemo).flatMap(OptionalDemo::getName).orElse(null);
String result4 = Optional.ofNullable(optionalDemo).flatMap(OptionalDemo::getNameOptional).orElse(null);
orElse(T other) orElseGet(Supplier extends T> other) orElseThrow(Supplier extends X> exceptionSupplier)
上述几个方法是用与Optional中的对象为空时所做的默认处理,最后者是用来抛异常。orElse 和 orElseGet区别
String result1 = Optional.ofNullable(optionalDemo).map(OptionalDemo::getName).orElse(new String("Hello"));
String result2 = Optional.ofNullable(optionalDemo).map(OptionalDemo::getName).orElseGet(() -> new String("Hello"));
前者不关心对象是否为空都会实例化一个String对象,后者是当Optional中的对象为空时才会实话化String对象。
推荐使用ofNullable(),在使用过程中完全可以避免空指针异常,另外还可以当数据为null时返回默认值;对于of()而言,当内部对象为null时在调用方法时会直接抛出。
对于orElse或者orElseGet,对于返回null或者不需要运算的默认处理可以使用orElse;而对于需要运算或者new出一个对象开辟新内存的运算是要使用orElseGet方法更好;
不推荐ifPresent的一下用法:
Optional<Test> testOptional = Optional.ofNullable(testService.getNameById("id"));
if(testOptional.ifPresent()){
Test test = testOptional.get();
......
}
------------------------------------------------------------------------------------------------------
Optional.ofNullable(testService.getNameById("id")).ifPresent(test -> {
......
})
其实我们不能说上述代码存在逻辑错误,我认为最起码对Optional的认识不够,没有完全领会Optional的初衷。实际上ifPresent()方法是配合of()使用的,ifPresent(Consumer super T> consumer)方法是配合ofNullable()使用的,前者是要求为null抛异常,后者为null做可以有开发者做出默认处理,所以既然你使用了ofNullable那么就不需要写if这种判断了;
Optional实际上就是一个外壳,这个外壳把对象包住,然后在外壳中做出一些运算,从而利用该外壳实现了对该对象的链式编程,简洁方便。Optional是有效消除if else的利器,千万不要浪费了Optional开发团队的好心,希望每个人都能正确使用吧。