你还在为空指针异常(NullPointerException)而烦恼吗?你在为判空的代码冗余而寻找新的解决方案吗?今天它来了:Java8中Optional类。
Optional类是Java8为了解决null值判断问题,借鉴google guava类库的Optional类而引入的一个同名Optional类。这时Java 8的Optional就发挥作用了,允许我们返回一个空的对象。
Optional类
在没有Optional之前,我们获取对象都需要判断,不然就会出现恶心的NPE。
// 获取学生的分数,容易出现空指针
int score = school.getClasses().getStudent().getScore();
// 加入判空之后的代码
if (school != null) {
Classes classes = school.getClasses();
if (classes != null) {
Student student = classes.getStudent();
if (student != null){
int score = student.getScore();
}
}
}
使用了optional之后会变成啥样呢?我们最后再看。这里,我们先看看Optional的部分源码:
public final class Optional<T> {
/**
* Common instance for {@code empty()}.
*/
private static final Optional<?> EMPTY = new Optional<>();
/**
* If non-null, the value; if null, indicates no value is present
*/
private final T value;
/**
* Constructs an empty instance.
*
* @implNote Generally only one empty instance, {@link Optional#EMPTY},
* should exist per VM.
*/
private Optional() {
this.value = null;
}
/**
* Returns an empty {@code Optional} instance. No value is present for this
* Optional.
*
* @apiNote Though it may be tempting to do so, avoid testing if an object
* is empty by comparing with {@code ==} against instances returned by
* {@code Option.empty()}. There is no guarantee that it is a singleton.
* Instead, use {@link #isPresent()}.
*
* @param <T> Type of the non-existent value
* @return an empty {@code Optional}
*/
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
/**
* Constructs an instance with the value present.
*
* @param value the non-null value to be present
* @throws NullPointerException if value is null
*/
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
/**
* Returns an {@code Optional} with the specified present non-null value.
*
* @param <T> the class of the value
* @param value the value to be present, which must be non-null
* @return an {@code Optional} with the value present
* @throws NullPointerException if value is null
*/
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
/**
* Returns an {@code Optional} describing the specified value, if non-null,
* otherwise returns an empty {@code Optional}.
*
* @param <T> the class of the value
* @param value the possibly-null value to describe
* @return an {@code Optional} with a present value if the specified value
* is non-null, otherwise an empty {@code Optional}
*/
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
//
// ......
}
通过上面的部分源码可以看出,Optional 的构造方法都是私有的。但是我们可以通过empty()
、of()
、ofNullable()
来创建Optional。Optional的源码比较简单,就是对判断对象多了一层封装,建议小伙伴们看看源码。
empty、ofNullable、of
我们先来说说结论:
empty
:创建一个包装对象值为空的Optional对象of
:创建包装对象值允许为空的Optional对象ofNullable
:创建包装对象值非空的Optional对象
实际开发中,看个人的选择,其实ofNullable
用的更多一些。
empty
// empty
Optional<Object> empty = Optional.empty();
// Optional的源码
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
empty
就是创建一个空的Optional实例。那你肯定会问,那这个没用啊,我想创建有值的呢?不着急,慢慢往后看。
of
// of
Optional<Object> of1 = Optional.of(1);
System.out.println(of1); // Optional[1]
Optional<Object> of2 = Optional.of("string");
System.out.println(of2); // Optional[string]
Optional<Object> of3 = Optional.of(null);
System.out.println(of3); // 抛出异常:java.lang.NullPointerException
// Optional部分源码
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
我们可以看出,Optional可以构造任意一个对象,Optional.of(T t)
传入一个非空的对象老构建一个Option。
ofNullable
// ofNullable
Optional<Integer> integer = Optional.ofNullable(1);
System.out.println(integer); // Optional[1]
Optional<String> string = Optional.ofNullable("string");
System.out.println(string); // Optional[string]
Optional<Object> o = Optional.ofNullable(null);
System.out.println(o); // Optional.empty
// Optional部分源码
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
我们可以看出,当传入的对象为null的时候,调用了empty()
构造,当对象不为空时,调用了of()
来进行构造,也就是为什么允许为空的构造。
get、isPresent
上面对象都构造出来了,那肯定需要判断对象是有值或者取值。
get
:get()方法主要用于返回包装对象的实际值。为null则抛出NPE。isPresent
:用于判断包装对象的值是否非空。
// get 从Optional获取值
Integer integer = Optional.ofNullable(1).get();
System.out.println(integer); // 1
String s = Optional.ofNullable("字符串").get();
System.out.println(s); // 字符串
Object o = Optional.ofNullable(null).get();
System.out.println(o); // 抛出异常 java.util.NoSuchElementException: No value present
// get的源码
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
// isPresent 判断Optional是否为空
boolean present = Optional.ofNullable(1).isPresent();
System.out.println(present); // true
boolean present1 = Optional.ofNullable(null).isPresent();
System.out.println(present1); // false
// isPresent的源码
public boolean isPresent() {
return value != null;
}
看着上面的代码,是不是感觉很简单。那有小伙伴说了,那我这样写:
Optional<User> u = Optional.ofNullable(user);
if (u.isPresent()){
return u.get().getUserId();
}
return null;
我看你真是大聪明,都这样了,那Optional还封装啥。不着急,我们慢慢往后看,有解决方法的。
orElse、orElseGet、orElseThrow
这三个都是在Optional不存在的前提下,进行的给默认值操作。
orElse
:当Optional为空时,默认的值。orElseGet
:当Optional为空时,默认的进行函数式操作的。orElseThrow
:当Optional为空时,抛出异常。
Student s = null;
// 当Student对象为空时,默认创建一个新的对象
Student student = Optional.ofNullable(s).orElse(new Student("1", "n=bearjun", "100"));
System.out.println(student);
// 当Student对象为空时,创建一个新的对象
Student student2 = Optional.ofNullable(s).orElseGet(() -> {
return new Student("1", "n=bearjun", "100");
});
System.out.println(student2);
// 当Student对象为空时,抛出异常
Optional.ofNullable(s).orElseThrow(() -> {
throw new RuntimeException("学生信息不存在");
});
// orElse的源码
public T orElse(T other) {
return value != null ? value : other;
}
// orElseGet的源码
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
// orElseThrow的源码
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
filter、map、flatMap
这三个都是在Optional存在的前提下,进行一些操作。
filter
:过滤值。接受一个Predicate参数,判断是否成立。map
:转换值。接受一个Function参数,通过传入的值转换操作成需要的值。flatMap
:接受一个Function参数,通过传入的值转换操作成需要的值。
filter
filter() 接受一个 Predicate 参数,返回测试结果为 true 的值。如果测试结果为 false,会返回一个空的 Optional。
// filter过滤
User user = new User();
Optional<User> user1 = Optional.ofNullable(user).filter(u -> "bearjun".equals(u.getUserId()));
System.out.println(user1.isPresent()); // false
// filter源码
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
map、flatMap
map() 对值Function作为参数的函数,然后将返回的值包装在 Optional 中。这样,改返回值进行链试调用的时候,orElse()可以对值进行操作。
相比于map,flatMap() 也需要Function作为参数,并对值调用这个函数,然后直接返回调用后的结果。
School school = new School(new Classes(new Student("1", "bearjun", "100")));
String s1 = Optional.ofNullable(school)
.map(School::getClasses)
.map(Classes::getStudent)
.map(Student::getScore)
.orElse("0");
System.out.println(s1); // 100
String s2 = Optional.ofNullable(school)
.flatMap(s -> {
return Optional.ofNullable(s.getClasses());
})
.flatMap(c -> {
return Optional.ofNullable(c.getStudent());
})
.flatMap(stu -> {
return Optional.ofNullable(stu.getScore());
})
.orElse("0");
System.out.println(s2); // 100
// 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));
}
}
// 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));
}
}
看到这里,相信小伙伴们已经知道了flatMap和map的区别。
map
的返回值是Optional<T>
。flatMap
相对于map
多了一层Optional<T>
封装。flatMap
的返回值是Optional<Optional<T>>
。
实战建议
实战一:当List查询为null时,默认返回一个空的集合
public List<Student> stuList() {
List<Student> stuList = studentMapper.list();
return Optional.ofNullable(stuList)
// 做一些其他的操作
//.map()
//.filter()
.orElse(new ArrayList<>());
}
反正我们的前端是一直在和我说这个问题,当集合为空时,最好默认返回一个空的集合。
没用Optional前那个很多if判断的代码改进
public int getScore(School school){
return Optional.ofNullable(school)
.map(School::getClasses)
.map(Classes::getStudent)
.map(Student::getScore)
.orElse("0");
}
这样写有点优雅,有没有。
总结
Optional是Java语言的对我们在代码中减少NullPointerExceptions的一个补充,并不能完全消除这些异常。
Optional虽然有的代码写起来很优雅和美观。但是代码的可读性较差,所以选择的时候还要考虑到自身的原因是否使用。
评论区