助你解读
最开始接触到这个类是在使用springdata的CrudRepository时遇到的,CrudRepository接口返回的单个对象类型的结果都是通过Optional打包的,出于Java开发习惯,看了下方法列表当中有个get方法,然后测试一波可用,就没有再深入研究了,直到出现了一连串的NoSuchElementException...于是习惯性的套上了try...,但是反过来一想,既然如此,那springdata为何还多此一举,打包成Optional来脱裤放屁。
一波面向搜索引擎之后,看了有十多篇文章,刚开始还很是疑惑,觉得跟StringUtils.isEmpty
方法差不多,这不就是把if打包成了一个方法吗,实则不然,通过函数式编程概念,就能理解其存在的合理性。
参考资料
- Oracle JDK API 参考文档 https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
- 菜鸟教程-Java 8 Optional 类 https://www.runoob.com/java/java8-optional-class.html
拓展资料
- Java8-函数式编程https://blog.csdn.net/icarusliu/article/details/79495534
- Java8 Optional 最佳实践(科学访问) https://medium.com/12-developer-labors/must-read-on-optional-type-b171e1b397bb
- 正确的使用Java8中的Optional类来消除代码中的null检查https://lw900925.github.io/java/java8-optional.html
- Java 8 中的 Optional 的优点究竟在哪里?https://www.zhihu.com/question/444199629
Java8新特性之Optional正确理解和用法
一、Optional 简介
Optional 是一个容器对象,可以存储对象、字符串等值,当然也可以存储 null 值。Optional 提供很多有用的方法,能帮助我们将 Java 中的对象等一些值存入其中,这样我们就不用显式进行空值检测,使我们能够用少量的代码完成复杂的流程。
比如它提供了:
-
of() 方法,可以将值存入 Optional 容器中,如果存入的值是 null 则抛异常。
-
ofNullable() 方法,可以将值存入 Optional 容器中,即使值是 null 也不会抛异常。
-
get() 方法,可以获取容器中的值,如果值为 null 则抛出异常。
-
getElse() 方法,可以获取容器中的值,如果值为 null 则返回设置的默认值。
-
isPresent() 方法,该方法可以判断存入的值是否为空。
-
…等等一些其它常用方法,下面会进行介绍。
可以说,使用 Optional 可以帮助我们解决业务中,减少值动不动就抛出空指针异常问题,也减少 null 值的判断,提高代码可读性等,这里我们介绍下,如果使用这个 Optional 类。
二、Optional 类描述
- Optional 类所在包: java.util.Optional
- Optional 类声明: public final class Optional extends Object
- **Optional 类方法:**同步上面参考资料中的JDK文档,由通天国际人工翻译,翻译不当请提交工单指正,谢谢。(【】中包含的内容为拓展介绍)
方法 | 修饰符&返回值类型 | 介绍 |
---|---|---|
empty() | static | 创建一个空的Optional实例 |
equals(Object obj) | boolean | 返回是否与this相等,是对应true |
filter(Predicate<? super T> predicate) | Optional | 如果存在且满足Predicate条件则返回自己,否则返回空 Optional。 |
flatMap(Function<? super T,Optional> mapper) | Optional | 如果值存在,返回function返回的结果,否则返回空Optional。 |
get() | T | 如果Optional包裹的值存在,则返回值,否则抛出NoSuchElementException 异常。【尽量不要主动调用,如果非要用,还要加上try-catch,不如直接if!=null】 |
hashCode() | int | 如果有值就返回值的hash码,没值返回0 |
ifPresent(Consumer<? super T> consumer) | void | 如果有值则调用Consumer,没值啥也不干。 |
isPresent() | boolean | 返回值是否存在,是对应true |
map(Function<? super T,? extends U> mapper) | Optional | 如果有值,则将提供的函数应用于该值,如果结果不为空,则返回包含新值的Optional |
of(T value) | static | 返回带有该值的Optional【如果传入null,会抛出NullPointerException异常】 |
ofNullable(T value) | static | 返回带有该值的Optional,如果传入null,返回空的Optional【如果传入null,不会抛出异常】 |
orElse(T other) | T | Optional中有值返回值,无值返回other |
orElseGet(Supplier<? extends T> other) | T | Optional中有值返回值,无值调用other,返回调用结果 |
orElseThrow(Supplier<? extends X> exceptionSupplier) | Optional中有值返回值,无值抛出你传入的异常 | |
toString() | String | 返回值的字符串形式,适用于调试 |
三、Optional 常用方法及使用示例
1、静态方法 Optional.of()
- 方法作用: 为指定的值创建一个指定非 null 值的 Optional。
- 方法描述: of 方法通过工厂方法创建 Optional 实例,需要注意的是传入的参数不能为 null,否则抛出 NullPointerException。
- **返回类型:**Optional
- 示例代码:
调用两个 Optional.of() 方法,一个传入正常参数,另一个传入 null 参数:
public static void main(String[] args) {
// 传入正常值,正常返回一个 Optional 对象
Optional<String> optional1 = Optional.of("mydlq");
// 传入参数为 null,抛出 NullPointerException.
Optional optional2 = Optional.of(null);
}
运行代码,可以观察到控制台输出内容如下:
Exception in thread "main" java.lang.NullPointerException
at java.util.Objects.requireNonNull(Objects.java:203)
at java.util.Optional.<init>(Optional.java:96)
at java.util.Optional.of(Optional.java:108)
at club.mydlq.OptionalExample.main(OptionalExample.java:12)
可以看到传入正常参数正常返回 Optional 对象,传入 null 参数返回 NullPointerException 异常。
2、静态方法 Optional.ofNullable()
- 方法作用: 为指定的值创建一个 Optional 对象,如果指定的参数为 null,不抛出异常,直接则返回一个空的 Optional 对象。
- 方法描述: ofNullable 方法是和 of 方式一样,都是用于创建 Optional 对象,只是传入的参数 null 时,会返回一个空的 Optional 对象,而不会抛出 NullPointerException 异常。
- 返回类型: Optional
- 示例代码:
调用 Optional.ofNullable() 方法,传入 null 参数:
public static void main(String[] args) {
// 传入正常值,正常返回一个 Optional 对象
Optional<String> optional1 = Optional.ofNullable("mydlq");
// 传入 null 参数,正常返回 Optional 对象
Optional optional2 = Optional.ofNullable(null);
}
运行代码,可以观察到正常传入值和传入 null 值时,都没有抛出异常。
3、对象方法 isPresent()
- 方法作用: 如果值存在则方法会返回 true,否则返回 false。
- 方法描述: 该方法其实就是用于判断创建 Optional 时传入参数的值是否为空,实现代码就简单一行,即
value != null
所以如果不为空则返回 true,否则返回 false。 - 返回类型: boolean
- 示例代码:
public static void main(String[] args) {
// 传入正常值,正常返回一个 Optional 对象,并使用 isPresent 方法
Optional optional1 = Optional.ofNullable("mydlq");
System.out.println("传入正常值返回:" + optional1.isPresent());
// 传入参数为 null 生成一个 Optional 对象,并使用 isPresent 方法
Optional optional2 = Optional.ofNullable(null);
System.out.println("传入 null 值返回:" + optional2.isPresent());
}
运行代码,可以观察到控制台输出内容如下:
传入正常值返回:true
传入 null 值返回:false
可以看到传入正常参数时调用 Optional 对象的 isPresent 方法时返回 true,传入 null 参数返回 false。
4、对象方法 get()
- 方法作用: 如果 Optional 有值则将其返回,否则抛出 NoSuchElementException 异常。
- 方法描述: get 方法内部实现其实就是判断 Otpional 对象中的 value 属性是否为 null,如果是就抛出 NoSuchElementException 异常,否则返回这个 value 值。
- 返回类型: T
- 示例代码:
public static void main(String[] args) {
// 传入正常值,正常返回一个 Optional 对象,并使用 get 方法获取值
Optional optional1 = Optional.ofNullable("mydlq");
System.out.println(optional1.get());
// 传入参数为 null 生成一个 Optional 对象,并使用 get 方法获取值
Optional optional2 = Optional.ofNullable(null);
System.out.println(optional2.get());
}
运行代码,可以观察到控制台输出内容如下:
传入正常参数:mydlq
Exception in thread "main" java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at club.mydlq.OptionalExample.main(OptionalExample.java:14)
BASH可以观察到传入正常值的 Optional 调用 get 方法正常输出值,通过空的 optional 对象使用 get 方法获取值时,抛出 NoSuchElementException 异常。
5、对象方法 ifPresent()
- 方法作用: 如果值存在则使用该值调用 consumer , 否则不做任何事情。
- 方法描述: 该方法 ifPresent(Consumer<? super T> consumer) 中参数接收的是 Consumer 类,它包含一个接口方法 accept(),该方法能够对传入的值进行处理,但不会返回结果。这里传入参数可以传入 Lamdda 表达式或 Consumer 对象及实现 Consumer 接口的类的对象。
- 返回类型: void
- 示例代码:
public static void main(String[] args) {
// 创建 Optional 对象,然后调用 Optional 对象的 ifPresent 方法,传入 Lambda 表达式
Optional optional1 = Optional.ofNullable("mydlq1");
optional1.ifPresent((value) -> System.out.println("Optional 的值为:" + value));
// 创建 Optional 对象,调用 Optional 对象的 ifPresent 方法,传入实现 Consumer 匿名内部类
Optional optional2 = Optional.ofNullable("mydlq2");
Consumer<String> consumer = new Consumer() {
@Override
public void accept(Object value) {
System.out.println("Optional 的值为:" + value);
}
};
optional2.ifPresent(consumer);
}
运行代码,可以观察到控制台输出内容如下:
Optional 的值为:mydlq1
Optional 的值为:mydlq2
可以观察到,调用 ifPresent 使用 lambda 或者内部匿名类方法,都是为了再执行 Optional 对象的 ifPresent 方法时,执行一段代码逻辑。
6、对象方法 orElse()
- 方法作用: 如果该值存在就直接返回, 否则返回指定的其它值。
- 方法描述: orElse 方法实现很简单,就是使用三目表达式对传入的参数值进行 null 验证,即
value != null ? value : other;
如果为 null 则返回 true,否则返回 false。 - 返回类型: T
- 示例代码:
public static void main(String[] args) {
// 传入正常参数,获取一个 Optional 对象,并使用 orElse 方法设置默认值
Optional optional1 = Optional.ofNullable("mydlq");
Object object1 = optional1.orElse("默认值");
System.out.println("如果值不为空:"+object1);
// 传入 null 参数,获取一个 Optional 对象,并使用 orElse 方法设置默认值
Optional optional2 = Optional.ofNullable(null);
Object object2 = optional2.orElse("默认值");
System.out.println("如果值为空:"+object2);
}
运行代码,可以观察到控制台输出内容如下:
如果值不为空:mydlq
如果值为空:默认值
可以观察到,如果 Optional 的值为空,则返回 orElse() 方法设置的默认值,否则返回 Optional 中的值。
7、对象方法 orElseGet()
- 方法作用: 如果该值存在就返回值,否则触发 other,并返回 other 调用的结果。
- 方法描述: orElseGet 方法和 orElse 方法类似,都是在 Optional 值为空时,返回一个默认操作,只不过 orElse 返回的是默认值,而 orElseGet 是执行 lambda 表达式,然后返回 lambda 表达式执行后的结果。
- 返回类型: T
- 示例代码:
public static void main(String[] args) {
// 传入正常参数,获取一个 Optional 对象,并使用 orElse 方法设置默认值
Optional optional1 = Optional.ofNullable("mydlq");
Object object1 = optional1.orElseGet(() -> {
String defaultVal = "执行逻辑和生成的默认值";
return defaultVal;
});
System.out.println("输出的值为:"+object1);
// 传入 null 参数,获取一个 Optional 对象,并使用 orElse 方法设置默认值
Optional optional2 = Optional.ofNullable(null);
Object object2 = optional2.orElseGet(() -> {
String defaultVal = "执行逻辑和生成的默认值";
return defaultVal;
});
System.out.println("输出的值为:"+object2);
}
运行代码,可以观察到控制台输出内容如下:
输出的值为:mydlq
输出的值为:执行逻辑和生成的默认值
可也观察到,当 Optional 值为不为空时正常返回带值的 Optional,如果 Optional 为空则返回 orElseGet 方法中 lambda 表达式执行后生成的值。
8、对象方法 orElseThrow()
- 方法作用: 如果 Optional 存在该值,返回包含的值,否则抛出由 Supplier 继承的异常。
- 方法描述: orElseThrow 方法其实就是判断创建 Optional 时传入的参数是否为 null,如果是非 null 则返回传入的值,否则抛出 异常。
- 返回类型: T
- 示例代码:
public static void main(String[] args) {
// 传入正常参数,获取一个 Optional 对象,并使用 orElseThrow 方法
try {
Optional optional1 = Optional.ofNullable("mydlq");
Object object1 = optional1.orElseThrow(() -> {
System.out.println("执行逻辑,然后抛出异常");
return new RuntimeException("抛出异常");
}
);
System.out.println("输出的值为:" + object1);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
// 传入 null 参数,获取一个 Optional 对象,并使用 orElseThrow 方法
try {
Optional optional2 = Optional.ofNullable(null);
Object object2 = optional2.orElseThrow(() -> {
System.out.println("执行逻辑,然后抛出异常");
return new RuntimeException("抛出异常");
}
);
System.out.println("输出的值为:" + object2);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
运行代码,可以观察到控制台输出内容如下:
值为不为空输出的值:mydlq
执行逻辑,然后抛出异常
java.lang.RuntimeException: 抛出异常
at club.mydlq.OptionalExample.lambda$main$1(OptionalExample.java:25)
at java.util.Optional.orElseThrow(Optional.java:290)
at club.mydlq.OptionalExample.main(OptionalExample.java:23)
可以观察到,当创建 Optional 时如果传入的参数为空则执行 Lambda 表达式代码逻辑后抛出异常信息,否则返回传入的参数值。
9、对象方法 map()
- 方法作用: 如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,则创建包含映射返回值的 Optional 作为 map 方法返回值,否则返回空 Optional。
- 方法描述: map 方法主要用于获取某个对象中的某个属性值的 Optional 对象时使用。map 方法调用时,首先验证传入的映射函数是否为空,如果为空则抛出异常。然后,再检测 Optional 的 value 是否为空,如果是,则返回一个空 value 的 Optional 对象。如果传入的映射函数和 Optinal 的 value 都不为空,则返回一个带 value 对象属性的 Optional 对象。
- 返回类型: Optional
- 示例代码:
示例1: 创建 Map 集合,存储一些键值对信息,通过 Optional 操作 Map 获取值,然后观察:
public static void main(String[] args) {
// 创建 map 对象
Map<String, String> userMap = new HashMap<>();
userMap.put("name1", "mydlq");
userMap.put("name2", null);
// 传入 Map 对象参数,获取一个 Optional 对象,获取 name1 属性
Optional<String> optional1 = Optional.of(userMap).map(value -> value.get("name1"));
// 传入 Map 对象参数,获取一个 Optional 对象,获取 name2 属性
Optional<String> optional2 = Optional.of(userMap).map(value -> value.get("name2"));
// 获取 Optional 的值
System.out.println("获取的 name1 的值:" + optional1.orElse("默认值"));
System.out.println("获取的 name2 的值:" + optional2.orElse("默认值"));
}
运行代码,可以观察到控制台输出内容如下:
获取的 Optional 的值:mydlq
获取的 Optional 的值:默认值
示例2: 创建一个用户类,使用 Optional 操作用户对象,获取其 name 参数,结合 Optional 的 map 方法获取值,进行观察:
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
使用 Optional 的 map 方法对值处理:
public static void main(String[] args) {
// 创建一个对象,设置姓名属性而不设置性别,这时候性别为 null
User user1 = new User("测试名称");
User user2 = new User();
// 使用 Optional 存储 User 对象
Optional<User> optional1 = Optional.ofNullable(user1);
Optional<User> optional2 = Optional.ofNullable(user2);
// 获取对象的 name 属性值
String name1 = optional1.map(User::getName).orElse("未填写");
String name2 = optional2.map(User::getName).orElse("未填写");
// 输出结果
System.out.println("获取的名称:" + name1);
System.out.println("获取的名称:" + name2);
}
运行代码,可以观察到控制台输出内容如下:
获取的名称:测试名称
获取的名称:未填写
总结:
通过上面两个示例观察到,通过 Optional 对象的 map 方法能够获取映射对象中的属性,创建 Optional 对象,并以此属性充当 Optional 的值,结合 orElse 方法,如果获取的属性的值为空,则设置个默认值。
10、对象方法 flatMap()
- 方法作用: 如果值存在,返回基于 Optional 包含的映射方法的值,否则返回一个空的 Optional。
- 方法描述: flatMap 方法和 map 方法类似,唯一的不同点就是 map 方法会对返回的值进行 Optional 封装,而 flatMap 不会,它需要手动执行 Optional.of 或 Optional.ofNullable 方法对 Optional 值进行封装。
- 返回类型: Optional
- 示例代码:
public static void main(String[] args) {
// 创建 map 对象
Map<String, String> userMap = new HashMap<>();
userMap.put("name", "mydlq");
userMap.put("sex", "男");
// 传入 Map 对象参数,获取一个 Optional 对象
Optional<Map<String, String>> optional1 = Optional.of(userMap);
// 使用 Optional 的 flatMap 方法,获取 Map 中的 name 属性
// 然后通过获取的值手动创建一个新的 Optional 对象
Optional optional2 = optional1.flatMap(value -> Optional.ofNullable(value.get("name")));
// 获取 Optional 的 value
System.out.println("获取的 Optional 的值:" + optional2.get());
}
运行代码,可以观察到控制台输出内容如下:
获取的 Optional 的值:mydlq
根据结果观察,可以看到 flatMap 和 map 方法没有什么区别,但是仔细看,代码中调用 flatMap 后,需要手动执行 of 或 ofNullable 方法创建了 Optional 对象。
11、对象方法 filter()
- 方法作用: 如果有值并且满足断言条件返回包含该值的 Optional,否则返回空 Optional。
- 方法描述: filter 方法通过传入的限定条件对 Optional 实例的值进行过滤,如果 Optional 值不为空且满足限定条件就返回包含值的 Optional,否则返回空的 Optional。这里设置的限定条件需要使用实现了 Predicate 接口的 lambda 表达式来进行配置。
- 返回类型: Optional
- 示例代码:
public static void main(String[] args) {
// 创建一个测试的 Optional 对象
Optional<String> optional = Optional.ofNullable("mydlq");
// 调用 Optional 的 filter 方法,设置一个满足的条件,然后观察获取的 Optional 对象值是否为空
Optional optional1 =optional.filter((value) -> value.length() > 2);
System.out.println("Optional 的值不为空::" + optional.isPresent());
// 调用 Optional 的 filter 方法,设置一个不满足的条件,然后观察获取的 Optional 对象值是否为空
Optional optional2 =optional.filter((value) -> value.length() <2);
System.out.println("Optional 的值不为空::" + optional2.isPresent());
}
运行代码,可以观察到控制台输出内容如下:
Optional 的值不为空:true
Optional 的值不为空:false
根据结果可以观察到,可以通过 filter 设置一个条件来判断 Optional 的值,如果满足条件就返回带值的 Optional,否则返回空的 Optional。
四、Optional 常用示例组合
在介绍一栏中已经说过 Optional 是个容器,它可用保存类型的 T 的值,即使 T 为 null 也可以使用 Optional 存储,这样我就不用显示进行空值检测,防止空指针异常。
上面也介绍了 Optional 的各种方法,在实际使用中这些方法常常组合使用。且很多方法也常与 Lambda 表达式结合,获取我们想要的结果的值。
下面是常用的示例,可以作为参考:
对集合中的对象属性进行过滤
创建一个 User 对象实体类,里面包含 name 属性:
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
创建一个使用 main 方法的类,创建几个 User 对象且设置不同的值,有的对象为 null 有的属性不设置,然后通过 Optional 获取 name 属性值加入集合,进行测试:
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class OptionalExample {
/**
* 测试的 main 方法
*/
public static void main(String[] args) {
// 创建一个测试的用户集合
List<User> userList = new ArrayList<>();
// 创建几个测试用户
User user1 = new User("abc");
User user2 = new User("efg");
User user3 = null;
// 将用户加入集合
userList.add(user1);
userList.add(user2);
userList.add(user3);
// 创建用于存储姓名的集合
List<String> nameList = new ArrayList();
// 循环用户列表获取用户信息,值获取不为空且用户以 a 开头的姓名,
// 如果不符合条件就设置默认值,最后将符合条件的用户姓名加入姓名集合
for (User user : userList) {
nameList.add(Optional.ofNullable(user).map(User::getName).filter(value -> value.startsWith("a")).orElse("未填写"));
}
// 输出名字集合中的值
System.out.println("通过 Optional 过滤的集合输出:");
nameList.stream().forEach(System.out::println);
}
}
输出运行结果:
通过 Optional 过滤的集合输出:
abc
未填写
未填写
通过上面,可以观察到,使用 Optional 有时候可以很方便的过滤一些属性,而且它的方法可以通过链式调用,方法间相互组合使用,使我们用少量的代码就能完成复杂的逻辑。
map经典判空之嵌套if
判断map不为空,map中的key-value不为null且不为空白字符串,常规玩法要么是一组嵌套if,要是是一个tay-catch,
public static void main(String[] args) {
System.out.println(test1());
System.out.println(test2());
System.out.println(test3());
System.out.println(test4());
}
public static String test1() {
//假装外部传入不可控map一个,比如sevlet接受前端传参
Map pm = new HashMap();
pm.put("key1", "value1");
Optional<Map> mapOptional = Optional.ofNullable(pm);
return mapOptional.map(map -> map.get("key1"))
.filter(value -> !"".equals(value))
.flatMap(value -> {
//一大堆业务处理...
return Optional.ofNullable("test1成功执行");
}).orElse("test1停车了");
}
public static String test2() {
//假装外部传入不可控map一个,比如sevlet接受前端传参
Map pm = new HashMap();
pm.put("key1", "");
Optional<Map> mapOptional = Optional.ofNullable(pm);
return mapOptional.map(map -> map.get("key1"))
.filter(value -> !"".equals(value))
.flatMap(value -> {
//一大堆业务处理...
return Optional.ofNullable("test2成功执行");
}).orElse("test2停车了");
}
public static String test3() {
//假装外部传入不可控map一个,比如sevlet接受前端传参
Map pm = new HashMap();
pm.put("key1", null);
Optional<Map> mapOptional = Optional.ofNullable(pm);
return mapOptional.map(map -> map.get("key1"))
.filter(value -> !"".equals(value))
.flatMap(value -> {
//一大堆业务处理...
return Optional.ofNullable("test3成功执行");
}).orElse("test3停车了");
}
public static String test4() {
//假装外部传入map一个,不传指定key
Map pm = new HashMap();
Optional<Map> mapOptional = Optional.ofNullable(pm);
return mapOptional.map(map -> map.get("key1"))
.filter(value -> !"".equals(value))
.flatMap(value -> {
//开始啪啪业务逻辑 发短信啊crud啊...
return Optional.ofNullable("test4成功执行");
}).orElse("test4停车了");
}
输出运行结果:
test1成功执行
test2停车了
test3停车了
test4停车了
标题:Java8新特性之Optional正确理解和用法
分类:Java
链接:https://www.aqwdzy.com/content/84
版权:通天技术网(www.aqwdzy.com)所分享发布内容,部分为网络转载,如有侵权请立即联系方式,我们第一时间删除并致歉!
本文转载或引用了以下内容:
http://www.mydlq.club/article/88/,https://www.jdon.com/idea/java/using-optional-effectively-in-java-8.html
真诚感谢以上无私奉献的作者,本站将一如既往秉承尊重原创的基本原则 ,呵护分享精神的火花。
热烈庆祝通天技术网开业大吉
@通天技术网 热烈庆祝通天技术网开业大吉
热烈庆祝通天技术网开业大吉
@通天技术网 热烈庆祝通天技术网开业大吉