java8—Optional
Optional 是什么
Optional 是 JAVA8 的一个新特性。Optional 类位于 java.util包下,那也就是说它其实是一个工具类。
我们看看官方是怎么介绍 Optional的。
java.util.Optional是一个可以包含或不可以包含非空值的容器对象,也就是说可以保存类型 为 T 的值,也可以仅仅保存 null 。如果值存在, 则 isPresent()方***返回 true,调用其 get()方***返回该值。
Optional 有什么用
Optional 类的引入主要是为了解决空指针异常(NullPointerException)。在我们平常开发中,我相信各位遇到最多的 Exception 也就是 NullPointerException了。各位可能图简单,可能会编写如下代码:
String cardNo = student.getCard().getCardNo().toUpperCase();
就上述代码出现 NullPointerException异常的情况实在是太多了,如果不想出现NullPointerException异常,那就要显示的进行空判断,如下:
String cardNoUpper = null;
if(student != null){
Card card = student.getCard();
if(card != null){
String cardNo = card.getCardNo();
if (cardNo != null){
cardNoUpper = cardNo.toUpperCase();
}
}
}
可以看到,只能在每个方法都加入非空检查,这样代码的阅读性和维护性都比较差。为了简化这个过程,我们就可以使用Optional了。Optional提供很多有用的方法,这样我们就可以不用显式的进行空值检测。
创建两个类,先来学习一下 Optional的使用方式和使用方法。
public class Student {
private String name;
private Integer age;
private Double grades;
// Card
private Card card;
//get set toString省略
}
public class Card {
private String cardNo;
private String cardName;
}
创建 Optional 实例
创建 Optional 实例的方式有三种,分别是of、empty、ofNullable 三个静态方法。接下来分别介绍一下。
- Optional.of(T t) : 创建一个 Optional 实例,t 必须非空; @Test public void test1(){ Optional<Student> student = Optional.of(new Student()); Student student1 = student.get(); System.out.println(student1); } 结果:Student{name='null', age=null, grades=null} 复制代码如果 of 方法的参数为 null,会出现空指针异常。该异常发生在创建 Optional 实例的时候。反例:
@Test
public void test1(){
Optional<Student> student2 = Optional.of(null);
}
- Optional.empty() : 创建一个空的 Optional 实例
如果硬是要创建一个 null 的 Optional 实例,可以使用该方法。
@Test
public void test2(){
Optional<Student> student = Optional.empty();
Student student1 = student.get();
System.out.println(student1);
}
该NoSuchElementException异常发生在 get() 方法被调用时。
- Optional.ofNullable(T t):t 可以为 null。如果非空,使用传入的值构建 Optional;否则返回空的Optional。
@Test
public void test3(){
Optional<Student> student = Optional.ofNullable(new Student());
Student student1 = student.get();
System.out.println(student1);
}
结果:Student{name='null', age=null, grades=null}
如果ofNullable参数传入 null,结果与 empty() 方法一致。源码如下:
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
学会了创建Optional实例的方法,接下来我们看看其他方法。
判断Optional容器中是否包含对象
- boolean isPresent()·: 判断是否包含值,如果值存在则返回 true,否则返回 false。
实例:
@Test
public void test4(){
Optional<Student> student = Optional.ofNullable(new Student());
if(student.isPresent()){
Student student1 = student.get();
System.out.println(student1);
}
Optional<Student> student2 = Optional.ofNullable(null);
//取反
if (!student2.isPresent()) {
System.out.println("该对象为空值");
}
}
结果:
Student{name='null', age=null, grades=null}
该对象为空值
- void ifPresent(Consumer<? super T> consumer) :与上述方法一字之差。如果有值,就执行 accept.accept;否则什么也不做。
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
实例:
@Test
public void test5(){
Optional<Student> student = Optional.ofNullable(new Student("新三", 18, 55.0));
student.ifPresent((x)-> {
x.setName("李四");
});
System.out.println(student.get());
}
结果:Student{name='李四', age=18, grades=55.0}
获取Optional容器的对象
- T get(): 如果调用对象包含值,返回该值,否则抛 NoSuchElementException异常
- T orElse(T other) :如果有值则将其返回,否则返回指定的对象
实例:
@Test
public void test6(){
Optional<Student> op = Optional.ofNullable(null);
// op 传入null,使用应返回我们指定的对象
Student st = op.orElse(new Student("新三", 18, 55.0));
System.out.println(st);
//传入Student
Optional<Student> op2 = Optional.ofNullable(new Student());
Student st2 = op2.orElse(new Student("新三", 18, 55.0));
System.out.println(st2);
}
Student{name='新三', age=18, grades=55.0}
Student{name='null', age=null, grades=null}
- T orElseGet(Supplier<? extends T> other) :如果有值则将其返回,否则返回由Supplier函数式接口提供的对象。
@Test
public void test7(){
Optional<Student> op = Optional.ofNullable(null);
Student st = op.orElseGet(()->{
//由于是一个函数式接口,里面可以写逻辑
Student student = new Student();
student.setAge(99);
student.setGrades(888.2);
student.setName("李四");
return student;
});
System.out.println(st);
}
结果:Student{name='李四', age=99, grades=888.2}k6a8
- T orElseThrow(Supplier<? extends X> exceptionSupplier) :如果有值则将其返回,否则抛出由Supplier函数式接口提供的异常
@Test
public void test8(){
Optional<Student> op = Optional.ofNullable(null);
Student st = op.orElseThrow(() -> new RuntimeException("值不存在"));
System.out.println(st);
}
- <U> Optional<U> map(Function<? super T, ? extends U> mapper):如果有值对其执行mapper.apply,并返回处理后的Optional。否则返回Optional.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));
}
}
实例:
@Test
public void test9(){
Optional<Student> op = Optional.ofNullable(new Student("新,三", 18, 55.0));
Optional<String[]> s1 = op.map(s -> s.getName())
.map(s -> s.split(","));
System.out.println(Arrays.toString(s1.get()));
}
结果:[新, 三]
- <U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper):与map类似,但是要求参数必须是Optional。
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));
}
}
实例:
@Test
public void test10(){
Optional<Student> op = Optional.ofNullable(new Student("新,三", 18, 55.0));
Optional<String[]> s1 = op.flatMap(s -> Optional.ofNullable(s.getName())
.flatMap(s2 -> Optional.ofNullable(s2.split(",")))
);
System.out.println(Arrays.toString(s1.get()));
}
结果:[新, 三]
- Optional<T> filter(Predicate<? super T> predicate):如果值存在,并且满足断言条件,就返回该值的Optional,否则返回Optional.empty
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
实例:
@Test
public void test11(){
Optional<Student> optional = Optional.of(new Student("新三", 18, 55.0));
// 不符合断言条件 返回 Optional.empty
Optional<Student> opt = optional.filter(s -> {
return "张".equals(s.getName());
});
System.out.println(opt.orElse(new Student("李四", 22, 223.0)));
}
结果:Student{name='李四', age=22, grades=223.0}
到此 Optional的提供的方法到此差不多介绍完毕了,接下来就是完善开篇提到的问题了,怎么使用Optional 来优化一下代码呢?
String cardNoUpper = null;
if(student != null){
Card card = student.getCard();
if(card != null){
String cardNo = card.getCardNo();
if (cardNo != null){
cardNoUpper = cardNo.toUpperCase();
}
}
}
首先重构一下Student 类内的 getCard方法,返回其 Optional 引用。
public class Student {
private String name;
private Integer age;
private Double grades;
private Card card;
// 其余省略。。。
public Optional<Card> getCard() {
return Optional.ofNullable(card);
}
public void setCard(Card card) {
this.card = card;
}
}
准备工作已经完成了,就开始编码吧!
public static void main(String[] args) {
Student student = new Student("新三", 18, 55.0);
Card cardObj = new Card("ACafgtc", "工商银行");
student.setCard(cardObj);
//Optional<Student> o = Optional.ofNullable(null);
Optional<Student> o = Optional.ofNullable(student);
String cardNoUpper = o
.flatMap(s -> s.getCard()) // 获得 Optional<Card>
.map(card -> card.getCardNo()) // 获得 Card 内的 cardNo
.map(cardNo -> cardNo.toUpperCase())
.orElse("默认值");
System.out.println(cardNoUpper);
}
结果:ACAFGTC
也可以直接将 Student设置为 null,看会不会出现异常。
public static void main(String[] args) {
Optional<Student> o = Optional.ofNullable(null);
String cardNoUpper = o
.flatMap(s -> s.getCard()) // 获得 Optional<Card>
.map(card -> card.getCardNo()) // 获得 Card 内的 cardNo
.map(cardNo -> cardNo.toUpperCase())
.orElse("默认值");
System.out.println(cardNoUpper);
}
结果:默认值
总结
对于 Optional这个类,有人觉得没必要去学,因为觉得在工作当中不会使用到它。想法其实是不正确的,虽然我们并不会将自己的方法返回值封装为Optional,但是学习Optional还是很有必要的,不说多的,至少要看得懂,会用。因为现在很多类库在使用Optional封装返回值,比如 Spring Data JPA、Stream API等。
- 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。