Java 8系列Java开发者的判空利器 -- Optional
Posted 善良勤劳勇敢而又聪明的老杨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 8系列Java开发者的判空利器 -- Optional相关的知识,希望对你有一定的参考价值。
热门系列:
-
【Java 8系列】收集器Collector与工具类Collectors
-
【Java 8系列】Stream详解,看这一篇就够啦
-
【Java 8系列】Lambda 表达式,一看就废
-
【Java 8系列】Java日期时间的新主宰者:LocalDate、LocalTime、LocalDateTime、ZonedDateTime
-
程序人生,精彩抢先看
目录
1.前序
JDK8是Oracle在2014年3月19日发布正式版的,和JDK7(2011年7月发布)相隔了近3年(拖的时间堪比JDK7和JDK6之间的时间,与历史版本发布间隔相比排在第二位,JDK6发布是2006,JDK7与之相比之间差了5年,这两个版本发布时间间隔最长,中间发生了Oracle收购SUN的大事件,JDK6因此曾成为使用率最高的JDK,),中间因意见不统一多次延迟。
距离现在已经近6年了。但是,目前仍然有很多公司在使用此版本作为开发版本!(至少我目前所呆的公司仍在使用,各位目前常用的是哪个版本呢?欢迎留言。。。)
但是为什么现在才拿出来记录呢。。。。当然是因为,我乐意啊,,哈哈。。(其实是最近有时间了,想要做一个Java 8系列的博文集啦)
最近,我会不定期,持续输出Java 8新特性且常用的一些技术点,例如Optional、Lambda表达式和函数式接口、Stream、日期时间API、重复注释、类型推断、 注解的扩展等等,与大家一起分享并记录!感兴趣,可以插个眼~~~!好了,今天先和大家一起扒一扒如题的判空类:Optional
2.正文
2.1 Optional的起源
著名的 NullPointerException 是引起系统失败最常见的原因。很久以前Google Guava项目引入了Optional作为解决空指针异常的一种方式,不赞成代码被null检查的代码污染,期望程序员写整洁的代码。受Google Guava的鼓励,Optional 现在是Java 8库的一部分。
Optional 只是一个容器,它可以保存一些类型的值或者null。它提供很多有用的方法,如官方文档API所示:
修饰符和类型 | 方法和说明 |
---|---|
static <T> Optional<T> | empty()
返回一个空Optional 实例。
|
boolean | equals(Object obj)
指示其他某个对象是否“等于”此Optional。
|
Optional<T> | filter(Predicate<? super T> predicate)
如果存在一个值,并且该值与给定的谓词匹配,则返回一个Optional 描述值的描述,否则返回一个empty Optional 。
|
<U> Optional<U> | flatMap(Function<? super T,Optional<U>> mapper)
如果存在值,则将提供的Optional -bearing映射函数应用于该值,返回该结果,否则返回empty Optional 。
|
T | get()
如果此值存在Optional ,则返回该值,否则抛出NoSuchElementException 。
|
int | hashCode()
返回当前值的哈希码值(如果有);如果没有值,则返回0(零)。
|
void | ifPresent(Consumer<? super T> consumer)
如果存在值,请使用该值调用指定的使用者,否则不执行任何操作。
|
boolean | isPresent()
true 如果存在值,则返回,否则返回false 。
|
<U> Optional<U> | map(Function<? super T,? extends U> mapper)
如果存在值,则将提供的映射函数应用于该值,如果结果为非null,则返回Optional 描述结果的描述。
|
static <T> Optional<T> | of(T value)
返回Optional 具有指定的当前非空值的。
|
static <T> Optional<T> | ofNullable(T value)
返回Optional 描述指定值的描述,如果不为null,则返回null Optional 。
|
T | orElse(T other)
返回值(如果存在),否则返回other 。
|
T | orElseGet(Supplier<? extends T> other)
返回值(如果存在),否则调用other 并返回该调用的结果。
|
<X extends Throwable> | orElseThrow(Supplier<? extends X> exceptionSupplier)
返回包含的值(如果存在),否则抛出异常,由提供的供应商创建。
|
String | toString()
返回此Optional的非空字符串表示形式,适用于调试。
|
但实际上,JDK1.8中Optional 总共有17个方法, 除去两个私有的构造方法, 除去equals, hashCode, toString三个方法外,;只剩下12个 Optional 提供的方法。
2.2 Optional使用场景
2.2.1 为什么要用?
在使用一个技术之前,我们必须要做一些思考,这样是很有必要的!能帮我更好的做到:知其然,知其所以然;(其实说简单点,就是防止少挖坑,骚年,醒醒吧!!你的bug还没有修复完~~~)
先说说为什么要用!!试想一下,你的代码或者你的小伙伴的代码中,你是否或多或少的见过类似如下的这些代码:
String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();
//将上面的代码进行判空处理
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.ofNullable(user)
.map(User::getAddress)
.map(Address::getCountry)
.map(Country::getIsocode)
.ifPresent(s-> s.toUpperCase());
如此,代码的可读性和整洁度,都好了许多!并且使用这种链式处理,也能更好的提高开发效率!这也是我们为什么需要用到optional的原因啦!
2.2.2 什么场景用?
我们写代码,在保证业务功能的正确和稳定性的基础之上,才会去考虑代码的可读性与简洁度,而不是一味的追求形式!!否则只会是华而不实,不仅容易出错,更是本末倒置!
个人总结了几点如下:
当使用值为空的情况,并非源于报错时产生,可以使用Optional(因为有错误的情况,肯定是不正常的,需要处理的)
对于一个对象,我们需要做判空、过滤、某些校验的时候,可以使用Optional
2.3 常用方法使用
方法介绍顺序,从简单的开始:
2.3.1 empty()
empty 方法返回一个不包含值的 Optional 实例, 注意不保证返回的 empty 是单例, 不要用 == 比较。
public static<T> Optional<T> empty();
2.3.2 of()
返回一个 Optional 实例;代表指定的非空值, 如果传入 null 会立刻抛出空指针异常。
public static <T> Optional<T> of(T value);
2.3.3 ofNullable()
- 返回一个 Optional 实例, 如果指定非空值则实例包含非空值, 如果传入 null 返回不包含值的 empty
public static <T> Optional<T> ofNullable(T value);
2.3.4 isPresent()
isPresent 用来判断实例是否包含值, 如果不包含非空值返回 false, 否则返回 true
public boolean isPresent();
2.3.5 get()
get 方法, 如果实例包含值则返回当前值, 否则抛出 NoSushElementException 异常
public T get();
2.3.6 ifPresent()
ifPresent 方法作用是当实例包含值时, 来执行传入的 Consumer, 比如调用一些其他方法
public void ifPresent(Consumer<? super T> consumer);
例如如下用法:
public void testIfPresent()
// ifPresent 表示 Optional 中的对象存在才会执行 Consumer 接口对象中的方法
List<String> dataList = new ArrayList<>();
// 1. 不为空没有值的集合
Optional.ofNullable(dataList)
.ifPresent(t ->
System.out.println("1"); // 输出 1
t.forEach(a -> System.out.println(a));
);
2.3.7 filter()
filter 方法用于过滤不符合条件的值, 接收一个 Predicate 参数, 如果符合条件返回代表值的 Optional 实例, 否则返回 empty;例如:
private static List<User> userList = new ArrayList<>();
/**
* 初始化 user 集合
*/
@Before
public void initEveryTestBefore()
userList.add(new User(22, "王旭", "wang.xu","123456", '1', true));
userList.add(new User(21, "孙萍", "sun.ping","a123456", '2', false));
userList.add(new User(23, "步传宇", "bu.zhuan.yu", "b123456", '1', false));
userList.add(new User(18, "蔡明浩", "cai.ming.hao","c123456", '1', true));
userList.add(new User(17, "郭林杰", "guo.lin.jie", "d123456", '1', false));
userList.add(new User(29, "韩凯", "han.kai", "e123456", '1', true));
userList.add(new User(22, "韩天琪", "han.tian.qi","f123456", '2', false));
userList.add(new User(21, "郝玮", "hao.wei","g123456", '2', false));
userList.add(new User(19, "胡亚强", "hu.ya.qing","h123456", '1', false));
userList.add(new User(14, "季恺", "ji.kai","i123456", '1', false));
userList.add(new User(17, "荆帅", "jing.shuai","j123456", '1', true));
userList.add(new User(16, "姜有琪", "jiang.you.qi","k123456", '1', false));
logger.info("initEveryTestBefore, size ", userList.size());
// filter: optional 中的对象在不为空,并且满足某个条件的时候才会返回
@Test
public void testFilter()
// 1. 在集合中有年龄大于 18 岁的才会返回所有对象
Optional.ofNullable(userList)
.filter(t -> t.stream().anyMatch(u -> u.getAge() > 18))
.ifPresent(t ->
t.forEach(u ->
System.out.println("1:" + u.toString());
);
);
// 2. 因为集合中没有年龄大于 50 岁的,因此不会返回任何对象
Optional.ofNullable(userList)
.filter(t -> t.stream().anyMatch(u -> u.getAge() > 50))
.ifPresent(t ->
t.forEach(u ->
System.out.println("2:" + u.toString());
);
);
2.3.8 map()
map 方法是链式调用避免空指针的核心方法, 当实例包含值时, 对值执行传入的 Function 逻辑, 并返回一个代表结果值的新的 Optional 实例;也就是将 optional 中的对象转成 其他对象,或者修改对象中的属性;如:
// 1. 返回 optional 中的对象年龄在 18 岁以上的
Optional.ofNullable(userList)
.map(t ->
List<User> tempList = new ArrayList<>();
t.forEach(u ->
if (u.getAge() > 18)
tempList.add(u);
);
return tempList;
)
.ifPresent(t ->
t.forEach(u ->
System.out.println("1:" + u.toString());
);
);
2.3.9 flatMap()
flatMap方法是将 optional 中的对象转成 optional 对象,或者修改对象中的属性;与map方法类似。不同之处在于:
Optional.ofNullable(userList)
.map(t ->
List<User> tempList = new ArrayList<>();
t.forEach(u ->
if (u.getAge() > 18)
tempList.add(u);
);
//不同之处
return Optional.of(tempList);
)
2.3.10 orElse()
orElse方法是如果实例包含值, 那么返回这个值, 否则返回指定的默认值, 如null
public T orElse(T other);
2.3.11 orElseGet()
orElseGet方法是如果实例包含值, 返回这个值;否则,它会执行作为参数传入的 Supplier(供应者) 函数式接口,并将返回其执行结果。
public T orElseGet(Supplier<? extends T> other);
orElse与orElseGet不同之处:
public void givenPresentValue_whenCompare_thenOk()
User user = new User("john@gmail.com", "1234");
logger.info("Using orElse");
User result = Optional.ofNullable(user).orElse(createNewUser());
logger.info("Using orElseGet");
User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
输出结果:
Using orElse
Creating New User
Using orElseGet
2.3.12 orElseThrow()
如果实例不包含值, 调用传入的 Supplier 参数, 生成一个异常实例并抛出.这个方法通常与全局异常处理器一起使用, 当参数或者其他情况获取不到值时, 抛出自定义异常, 由异常处理器处理成通用返回结果, 返回给前端;如:
Optional.ofNullable(tempList)
.orElseThrow(() -> runtimeException)
.forEach(t -> System.out.println("2:" + t));
3.总结
以上即为JDK1.8新增的Optional类的常用方法及场景记录!举例的代码方面,比较零散,但主要是为了方便大家有一个功能使用和方法差别上的了解!(所以,不要在意那些鸡零狗碎的代码意义~~~~)
路漫漫,其修远兮!欢迎大家提问、留言!!!
微信同款:https://mp.weixin.qq.com/s/jhJBI6uK6bvCBTUxnj-LUA
以上是关于Java 8系列Java开发者的判空利器 -- Optional的主要内容,如果未能解决你的问题,请参考以下文章
Java 8系列收集器Collector与工具类Collectors
Java 8系列Java日期时间的新主宰者:LocalDateLocalTimeLocalDateTimeZonedDateTime