java8 Optional

一、前言

日常开发中,我们经常会遇到空指针异常(NPE)。可以说,NPE是我们最头疼的问题之一了。为了比变空指针,我们一般会加上判空操作。

如下:

 1 class Teacher {
 2     private Integer id;
 3     private String name;
 4 }
 5 
 6 class Student {
 7     private Integer id;
 8     private String name;
 9     private Teacher teacher;
10 }

当我们调用

1 student.getTeacher().getId()

上面这种写法,很容易产生NullPointerException异常的。为了解决这个问题,于是采用下面的写法:

1 if(student!=null){
2     Teacher t = student.getTeacher();
3     if(t!=null){
4         Integer id = t.getId();
5     }
6 }    

为了简化上面的写法,我们可以利用Java8的新特性 Optional 来尽量简化代码同时高效处理NPE

 

 

二、Optional类使用解析

1、optional介绍

  • Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
  • Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
  • Optional 类的引入很好的解决空指针异常。

 

2、构建Optional

构建一个Optional对象,有这些方法:

 

 

  • Optional(T value)

 

  • empty()
  • of(T value)
  • ofNullable(T value)

 

先说明一下,Optional(T value),即构造函数,它是private权限的,不能由外部调用的。其余三个函数是public权限,供我们所调用。那么,Optional的本质,就是内部储存了一个真实的值,在构造的时候,就直接判断其值是否为空。好吧,这么说还是比较抽象。直接上Optional(T value)构造函数的源码,如下图所示:

 

 

 

of(T value)的源码如下

1 public static <T> Optional<T> of(T value) {
2     return new Optional<>(value);
3 }

也就是说of(T value)函数内部调用了构造函数。根据构造函数的源码我们可以得出两个结论:

  • 通过of(T value)函数所构造出的Optional对象,当Value值为空时,依然会报NullPointerException。
  • 通过of(T value)函数所构造出的Optional对象,当Value值不为空时,能正常构造Optional对象。

除此之外呢,Optional类内部还维护一个value为null的对象,大概就是长下面这样的

 1 public final class Optional<T> {
 2     //省略....
 3     private static final Optional<?> EMPTY = new Optional<>();
 4     private Optional() {
 5         this.value = null;
 6     }
 7     //省略...
 8     public static<T> Optional<T> empty() {
 9         @SuppressWarnings("unchecked")
10         Optional<T> t = (Optional<T>) EMPTY;
11         return t;
12     }
13 }

那么,empty()的作用就是返回EMPTY对象。

好了铺垫了这么多,可以说ofNullable(T value)的作用了,上源码

1 public static <T> Optional<T> ofNullable(T value) {
2     return value == null ? empty() : of(value);
3 }

相比较of(T value)的区别就是,当value值为null时,of(T value)会报NullPointerException异常;ofNullable(T value)不会throw Exception,ofNullable(T value)直接返回一个EMPTY对象。

那是不是意味着,我们在项目中只用ofNullable函数而不用of函数呢?不是的,一个东西存在那么自然有存在的价值。当我们在运行过程中,不想隐藏NullPointerException。而是要立即报告,这种情况下就用of函数。

 

3、Optional API 及源码注解

 

 

 

  1 package java.util;
  2 
  3 import java.util.function.Consumer;
  4 import java.util.function.Function;
  5 import java.util.function.Predicate;
  6 import java.util.function.Supplier;
  7 import java.util.stream.Stream;
  8 
  9 /**
 10 一个可能包含也可能不包含非null值的容器对象。 如果存在值,则isPresent()返回true 。 如果不存在任何值,则该对象被视为空,并且isPresent()返回false 。
 11 提供了其他取决于所包含值是否存在的方法,例如orElse() (如果不存在值,则返回默认值)和ifPresent() (如果存在值,则执行操作)。
 12 这是一个基于值的类; 在Optional实例上使用标识敏感的操作(包括引用等于( == ),标识哈希码或同步)可能会产生不可预测的结果,应避免使用
 13  */
 14 public final class Optional<T> {
 15     /**
 16      * empty()通用实例
 17      */
 18     private static final Optional<?> EMPTY = new Optional<>();
 19 
 20     /**
 21      * 如果不为空,则为该值;否则为false。 如果为null,则表示不存在任何值
 22      */
 23     private final T value;
 24 
 25     /**
 26         构造一个空实例。
 27         impl注意:
 28         通常,每个VM仅应存在一个空实例EMPTY 
 29      */
 30     private Optional() {
 31         this.value = null;
 32     }
 33 
 34     /**
 35         返回一个空的Optional实例。 此Optional没有值。
 36         类型参数:<T> –不存在的值的类型
 37         返回值:一个空的Optional
 38         api注意:
 39         尽管这样做可能很诱人,但应通过将==与Optional.empty()返回的实例进行比较来避免测试对象是否为空。 不能保证它是一个单例。 而是使用isPresent() 
 40      */
 41     public static<T> Optional<T> empty() {
 42         @SuppressWarnings("unchecked")
 43         Optional<T> t = (Optional<T>) EMPTY;
 44         return t;
 45     }
 46 
 47     /**
 48         使用描述的值构造一个实例。
 49         参数:值–要描述的非null值
 50         抛出:NullPointerException如果值为null
 51      */
 52     private Optional(T value) {
 53         this.value = Objects.requireNonNull(value);
 54     }
 55 
 56     /**
 57         返回一个Optional描述给定的非null值。
 58         参数:value –要描述的值,必须为非null
 59         类型参数:<T> –值的类型
 60         返回值:存在值的Optiona
 61      */
 62     public static <T> Optional<T> of(T value) {
 63         return new Optional<>(value);
 64     }
 65 
 66     /**
 67         返回一个描述给定值的Optional ,如果不为null ,则返回一个空的Optional 。
 68         参数:值–描述的可能为null值
 69         类型参数:<T> –值的类型
 70         返回值:一个Optional与如果指定值是非当前值null ,否则一个空Optional
 71      */
 72     public static <T> Optional<T> ofNullable(T value) {
 73         return value == null ? empty() : of(value);
 74     }
 75 
 76     /**
 77         如果存在值,则返回该值,否则抛出NoSuchElementException 。
 78         返回值:此Optional描述的非null值
 79         抛出:NoSuchElementException如果不存在任何值
 80         api注意:此方法的首选替代方法是orElseThrow() 。
 81      */
 82     public T get() {
 83         if (value == null) {
 84             throw new NoSuchElementException("No value present");
 85         }
 86         return value;
 87     }
 88 
 89     /**
 90         如果存在值,则返回true ,否则返回false 。
 91 
 92         返回值:如果存在值,则为true ,否则为false
 93      */
 94     public boolean isPresent() {
 95         return value != null;
 96     }
 97 
 98     /**
 99         如果不存在值,则返回true ,否则返回false 。
100 
101         返回值:如果不存在值,则为true ,否则为false
102      */
103     public boolean isEmpty() {
104         return value == null;
105     }
106 
107     /**
108         如果存在值,则使用该值执行给定的操作,否则不执行任何操作。
109         参数:动作–要执行的动作(如果存在值)
110      */
111     public void ifPresent(Consumer<? super T> action) {
112         if (value != null) {
113             action.accept(value);
114         }
115     }
116 
117     /**
118         如果存在值,则使用该值执行给定的操作,否则执行给定的基于空的操作。
119         参数:动作–要执行的动作(如果存在值)emptyAction –要执行的基于空的操作(如果不存在任何值)
120         抛出:NullPointerException如果存在一个值并且给定的操作为null ,或者不存在任何值并且给定的基于空的操作为null 
121          @since 9
122      */
123     public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
124         if (value != null) {
125             action.accept(value);
126         } else {
127             emptyAction.run();
128         }
129     }
130 
131     /**
132         如果存在一个值,并且该值与给定的谓词匹配,则返回描述该值的Optional ,否则返回一个空的Optional 。
133 
134         参数:谓词–应用于值的谓词(如果存在)
135         返回值:一个Optional描述此的值Optional ,如果一个值存在并且该值给定的谓词相匹配,否则一个空Optional
136         抛出:NullPointerException如果谓词为null
137      */
138     public Optional<T> filter(Predicate<? super T> predicate) {
139         Objects.requireNonNull(predicate);
140         if (!isPresent()) {
141             return this;
142         } else {
143             return predicate.test(value) ? this : empty();
144         }
145     }
146 
147     /**
148         如果存在值,则返回一个Optional描述(就像by ofNullable ),将给定映射函数应用于该值的结果,否则返回一个空的Optional 。
149         如果映射函数返回null结果,则此方法返回空的Optional 
150      */
151     public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
152         Objects.requireNonNull(mapper);
153         if (!isPresent()) {
154             return empty();
155         } else {
156             return Optional.ofNullable(mapper.apply(value));
157         }
158     }
159 
160     /**
161          如果存在一个值,则返回将给定Optional -bearing映射函数应用于该值的结果,否则返回一个空的Optional 。
162         此方法类似于map(Function) ,但是映射函数是其结果已经是Optional函数,如果调用该函数,则flatMap不会将其包装在其他Optional 。
163 
164         参数:mapper –应用于值的映射函数(如果存在)
165         类型参数:<U> –映射函数返回的Optional值的类型
166         返回值:施加的结果Optional荷瘤映射函数此的值Optional ,如果一个值存在,否则一个空Optional
167         抛出:NullPointerException如果映射函数为null或返回null结果
168 
169      */
170     public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) {
171         Objects.requireNonNull(mapper);
172         if (!isPresent()) {
173             return empty();
174         } else {
175             @SuppressWarnings("unchecked")
176             Optional<U> r = (Optional<U>) mapper.apply(value);
177             return Objects.requireNonNull(r);
178         }
179     }
180 
181     /**
182         如果值存在时,返回一个Optional描述的值,否则将返回一个Optional产生通过供给功能。
183 
184         参数:供应商–产生要返回的Optional的供应功能
185         返回值:返回一个Optional描述此的值Optional ,如果一个值存在,否则Optional所生产的供应功能。
186         抛出:NullPointerException如果提供的函数为null或产生null结果
187      * @since 9
188      */
189     public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) {
190         Objects.requireNonNull(supplier);
191         if (isPresent()) {
192             return this;
193         } else {
194             @SuppressWarnings("unchecked")
195             Optional<T> r = (Optional<T>) supplier.get();
196             return Objects.requireNonNull(r);
197         }
198     }
199 
200     /**
201         如果存在值,则返回仅包含该值的顺序Stream ,否则返回空Stream 。
202 
203         返回值:作为Stream的可选值
204      * @since 9
205      */
206     public Stream<T> stream() {
207         if (!isPresent()) {
208             return Stream.empty();
209         } else {
210             return Stream.of(value);
211         }
212     }
213 
214     /**
215         如果存在值,则返回该值,否则返回other 。
216 
217         参数:其他–要返回的值(如果不存在任何值)。 可以为null 。
218         返回值:值(如果存在),否则other
219      */
220     public T orElse(T other) {
221         return value != null ? value : other;
222     }
223 
224     /**
225         如果存在值,则返回该值,否则返回由供应函数产生的结果。
226 
227         参数:供应商–产生要返回的值的供应函数
228         返回值:值(如果存在),否则提供功能产生的结果
229      */
230     public T orElseGet(Supplier<? extends T> supplier) {
231         return value != null ? value : supplier.get();
232     }
233 
234     /**
235      * If a value is present, returns the value, otherwise throws
236      * {@code NoSuchElementException}.
237      *
238      * @return the non-{@code null} value described by this {@code Optional}
239      * @throws NoSuchElementException if no value is present
240      * @since 10
241      */
242     public T orElseThrow() {
243         if (value == null) {
244             throw new NoSuchElementException("No value present");
245         }
246         return value;
247     }
248 
249     /**
250         如果存在值,则返回该值,否则抛出由异常提供函数产生的异常。
251 
252         参数:exceptionSupplier –产生要抛出的异常的提供函数
253         类型参数:<X> –引发的异常类型
254         返回值:值(如果存在)
255         抛出:X –如果不存在任何值NullPointerException如果不存在任何值并且异常提供函数为null
256         api注意:带有空参数列表的对异常构造函数的方法引用可用作提供者
257      */
258     public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
259         if (value != null) {
260             return value;
261         } else {
262             throw exceptionSupplier.get();
263         }
264     }
265 }

 

 

三、测试使用

1、构建

 1     public void test01() {
 2         //返回一个描述给定值的Optional ,如果不为null ,则返回一个空的Optional 。
 3         //参数:值–描述的可能为null值
 4         //类型参数:<T> –值的类型
 5         //返回值:一个Optional与如果指定值是非当前值null ,否则一个空Optional
 6         Optional s1 = Optional.ofNullable(null);
 7 
 8         // 构建一个value不可以为null的optional对象,如果of()的入参为null会报空指针异常;
 9         Optional<MyUser> s2 = Optional.of(new MyUser("张三", "123456"));
10 
11         // 构建一个value可以为null的optional对象;
12         Optional<MyUser> s3 = Optional.ofNullable(null);
13 
14         System.out.println(s1);
15         System.out.println(s2);
16         System.out.println(s3);
17 
18         /* 输出
19          * Optional.empty
20          * Optional[TestRegex.MyUser(name=阿辉2, id=123456)]
21          * Optional.empty
22          **/
23     }

 

2、判断类

 1 public void test02(){
 2         Optional<MyUser> myUser1 = Optional.empty();
 3         Optional<MyUser> myUser2 = Optional.of(new MyUser("李四", "123456"));
 4 
 5         // filter传入一个lambda,lambda返回值为boolean;true:不做任何改变,false:返回一个空的optional;
 6         Optional<MyUser> myUser3 = myUser2.filter(user -> "李四".equals(user.getName()));
 7 
 8         // isPresent就是判断value是不是null;我们在调用get之前,一定要先调用isPresent,因为直接如果value是null,直接调用get会报异常;
 9         if (myUser1.isPresent()) {
10             MyUser value = myUser1.get();
11             System.out.println("optional value:" + value);
12         } else {
13             System.out.println("optional value==null");
14         }
15         // ifPresent传入一段lambda,当value!=null时,执行里面的逻辑;当当value==null时,啥都不干;
16         myUser2.ifPresent(value -> System.out.println("optional value:" + value));
17         
18         System.out.println(myUser3);
19 
20         // 输入如下:
21         //  optional value==null
22         //  optional value:TestOptional.MyUser(name=李四, id=123456)
23         //  Optional[TestOptional.MyUser(name=李四, id=123456)]
24     }

 

3、获取类

 1     public void test03(){
 2         Optional<MyUser> userInfoEmptyOpt = Optional.empty();
 3         Optional<MyUser> userInfoOpt = Optional.of(new MyUser("张三","123456"));
 4 
 5         // 1、直接获取,注意如果value==null,会报NoSuchElementException异常
 6         MyUser userInfo1 = userInfoOpt.get();
 7 
 8         // 2、orElse可以传入一个UserInfo类型的对象作为默认值;
 9         //    当value!=null时,返回value值;当value==null时,返回默认值作为代替;
10         MyUser userInfo2 = userInfoEmptyOpt.orElse(new MyUser("张三1","123456"));
11 
12         // 3、orElseGet和orElse不同的是orElseGet可以传入一段lambda表达式;
13         // 当value!=null时,返回value值;
14         // 当value==null时,使用该lambda返回的对象作为默认值;
15         MyUser userInfo3 = userInfoEmptyOpt.orElseGet(() -> new MyUser("张三2","123456"));
16 
17         // 4、orElseThrow可以传入一段lambda表达式,lambda返回一个Exception;当value!=null时,返回value值;当value==null时,抛出该异常;
18         MyUser userInfo4 = userInfoOpt.orElseThrow(NullPointerException::new);
19 
20         System.out.println(userInfo1);
21         System.out.println(userInfo2);
22         System.out.println(userInfo3);
23         System.out.println(userInfo4);
24 
25         // 输出如下:
26         // TestOptional.MyUser(name=张三, id=123456)
27         // TestOptional.MyUser(name=张三1, id=123456)
28         // TestOptional.MyUser(name=张三2, id=123456)
29         // TestOptional.MyUser(name=张三, id=123456)
30     }

 

4、转换类

 1     public void test04(){
 2         Optional<MyUser> userInfoOpt = Optional.of(new MyUser("张三","123456"));
 3 
 4         // 原来value的类型是UserInfo,经过map转换为Optional<String>
 5         Optional<String> username = userInfoOpt.map(MyUser::getName);
 6 
 7         // 当map的入参也是一个Optional时,经过map转化后会形成Optional<Optional<String>>这种嵌套结构;但flatMap可以把这种嵌套结构打平;
 8         Optional<Optional<String>> unFlatMap = userInfoOpt.map(user -> Optional.of(user.getName()));
 9         Optional<String> flatMap = userInfoOpt.flatMap(user -> Optional.of(user.getName()));
10 
11         System.out.println(username);
12         System.out.println(unFlatMap);
13         System.out.println(flatMap);
14 
15         // 输出如下:
16         // Optional[张三]
17         // Optional[Optional[张三]]
18         // Optional[张三]
19     }

 

 

四、总结

回到前言的那个问题,我们可以用如下代码替换:

1     public String getId(Student student) throws Exception{
2         return Optional.ofNullable(student)
3                 .map(u-> u.getTeacher())
4                 .map(a->a.getId())
5                 .orElseThrow(()->new Exception("取值错误"));
6     }

其他的例子,不一一列举了。不过采用这种链式编程,虽然代码优雅了。但是,逻辑性没那么明显,可读性有所降低,大家项目中看情况酌情使用。

posted @ 2022-04-25 14:42  r1-12king  阅读(35)  评论(0编辑  收藏  举报