Java8中Optional类的使用说明
目录
- 简介
- 历史
- null带来的种种问题
- 方案
- 场景引入
- 方法说明
- 构造函数
- 创建Optional对象
- 使用map从Optional对象中提取和转换值
- 使用flatMap链接Optional对象
- 默认行为及解引用Optional对象1
- 默认行为及解引用Optional对象2
- 使用filter剔除特定的值
- 实战
- 总结
简介
optional类是java8中引入的针对NPE问题的一种优美处理方式,源码作者也希望以此替代null。
历史
1965年,英国一位名为Tony Hoare的计算机科学家在设计ALGOL W语言时提出了null引用的想法。Hoare选择null引用这种方式,“只是因为这种方法实现起来非常容易”。很多年后,他开始为自己曾经做过这样的决定而后悔不迭,把它称为“我价值百万的重大失误”。我们已经看到它带来的后果——程序员对对象的字段进行检查,判断它的值是否为期望的格式,最终却发现我们查看的并不是一个对象,而是一个空指针,它会立即抛出一个让人厌烦的NullPointerException异常[1]。
null带来的种种问题
- 错误之源。
- 代码膨胀。
- 自身是毫无意义的。
- 破坏了Java的哲学。
- 在Java的类型系统上开了个口子。
方案
汲取Haskell和Scala的灵感,Java 8中引入了一个新的类java.util.Optional
场景引入
首先我们引入一个常见的两个场景
/** * >1 引入,常规判断一个学生的学校是不是公立学校,判断是否成年 */public static boolean checkIsPublicV1(Student student) {if (student != null) {School school = student.getSchool(); if (school != null) {return school.isPublicFlag(); }}throw new RuntimeException("参数异常"); }public static String getAdultV1(Student student) {if (student != null) {int age = student.getAge(); if (age > 18) {return student.getName(); }}return "无"; }
上述方式是我们常见的判读流程,optional就是针对每次空判断导致的代码欣赏性问题进行了一套解决方案
方法说明
文章图片
构造函数
- Optional(T var1)
private final T value; private Optional() {this.value = https://www.it610.com/article/null; }private Optional(T var1) {this.value = Objects.requireNonNull(var1); }
从源码可知,optional的构造器私有,不能直接创建,只能通过类中的其他静态方法创建,optional构造器支持一个泛型入参,且改参数不能为空
创建Optional对象
- 声明一个空的Optional: Optional
empty() - 依据一个非空值创建Optional: Optional
of(T var0) - 可接受null的Optional: Optional
ofNullable(T var0)
empty()源码
private static final Optional> EMPTY = new Optional(); private Optional() {this.value = https://www.it610.com/article/null; }public staticOptional empty() {Optional var0 = EMPTY; return var0; }
从源码可知,optional类中维护了一个null值的对象,使用empty静态方法即可返回该空值对象
of(T var0)源码
public staticOptional of(T var0) {return new Optional(var0); }
返回常见的有参构造对象,注意由于构造器要求入参不能为空,因此of方法的入参为空的话,依然会报NPE异常
ofNullable(T var0)源码
public staticOptional ofNullable(T var0) {return var0 == null ? empty() : of(var0); }
这个方法是对of方法的补强,当ofNullable方法的入参不为空是正常返回构造对象,当入参为空时,返回一个空值的optional对象,而不会抛出异常。
ofNullable方法大部分场景优于of的使用。
null引用和Optional.empty()有什么本质的区别吗?从语义上,你可以把它们当作一回事儿,但是实际中它们之间的差别非常大:如果你尝试解引用一个null,一定会触发NullPointerException,不过使用Optional.empty()就完全没事儿,它是Op-tional类的一个有效对象,多种场景都能调用,非常有用。举例
public static void testOptionalBuild() {// 1. 无法直接new 'Optional()' has private access in 'java.util.Optional'// Optional school = new Optional<>(); // 2. of构建非空和空对象(NullPointerException)/*Optional o1 = Optional.of("test"); System.out.println(o1); Optional
使用map从Optional对象中提取和转换值
- Optional map(Function super T, ? extends U> var1)
public Optional map(Function super T, ? extends U> var1) {Objects.requireNonNull(var1); return !this.isPresent() ? empty() : ofNullable(var1.apply(this.value)); }
当optional包裹的值为空时直接放回空对象,否则执行入参中的Function.apply方法
使用flatMap链接Optional对象
- Optional flatMap(Function super T, Optional> var1)
public Optional flatMap(Function super T, Optional> var1) {Objects.requireNonNull(var1); return !this.isPresent() ? empty() : (Optional)Objects.requireNonNull(var1.apply(this.value)); }
与map几乎一致。注意的是,入参的Function.apply方法中,返回类型为optional类型
举例
public static void testOptionalMap() {Student student1 = getDefaultStudent(); Student student2 = getBackStudent(); // mapString school1 = Optional.ofNullable(student1).map(i -> i.getName()).orElse("无名"); String school2 = Optional.ofNullable(student2).map(i -> i.getName()).orElse("无名"); System.out.println("school1: " + school1 + "| school2: " + school2); // flapMap 链式String school3 = Optional.ofNullable(getOptionalStudent()).flatMap(i -> getOptionalStudent()).flatMap(i->i.getSchool()).map(i->i.getSchoolName()).orElse("没上大学"); System.out.println("school3: " + school3); }
默认行为及解引用Optional对象1
- T orElse(T var1)
- T orElseGet(Supplier extends T> var1)
- T orElseThrow(Supplier extends X> var1)
源码
orElse源码
public T orElse(T var1) {return this.value != null ? this.value : var1; }
当optional中的包裹值不为空时返回包裹的值,若为空则返回orElse中的入参值
orElseGet源码
public T orElseGet(Supplier extends T> var1) {return this.value != null ? this.value : var1.get(); }public T get() {if (this.value =https://www.it610.com/article/= null) {throw new NoSuchElementException("No value present"); } else {return this.value; }}
与上个方法类似,当optional中的包裹值不为空时返回包裹的值,若为空执行orElseGet中的Supplier方法
orElseThrow源码
publicT orElseThrow(Supplier extends X> var1) throws X {if (this.value != null) {return this.value; } else {throw (Throwable)var1.get(); }}
类似的,当optional中的包裹值不为空时返回包裹的值,若为空抛出orElseThrow中的Supplier.get的异常方法
举例
public static void testOptionalOrElse() {// orElseStudent stu = getDefaultStudent(); Student backStudent = getBackStudent(); Student realStu1 = Optional.ofNullable(stu).orElse(backStudent); System.out.println(realStu1); // orElseGetStudent realStu2 = Optional.ofNullable(stu).orElseGet(()-> getBackStudent()); System.out.println(realStu2); // orElseGetStudent realStu3 = Optional.ofNullable(stu).orElseThrow(()-> new RuntimeException("学生不存在")); System.out.println(realStu3); }
默认行为及解引用Optional对象2
- boolean isPresent()
- void ifPresent(Consumer super T> var1)
isPresent()源码public boolean isPresent() {return this.value != null; }
用户判断optional包裹的值是否为空,返回布尔值
ifPresent(Consumer var1)源码
public void ifPresent(Consumer super T> var1) {if (this.value != null) {var1.accept(this.value); }}
用户处理optional包裹的值不为空时,继续处理入参中Consumer.accept的方法。类似于 if(var!=null) {do sth}
举例
public static void testOptionalIfPresent() {// isPresent()Student student1 = getDefaultStudent(); Student student2 = getBackStudent(); boolean b1 = Optional.ofNullable(student1).isPresent(); boolean b2 = Optional.ofNullable(student2).isPresent(); System.out.println("b1: " + b1 + "| b2: " + b2); // isPresent(Consumer)Optional.ofNullable(student2).ifPresent(i-> acceptStudent(i, LocalDate.now())); }
使用filter剔除特定的值
- Optional filter(Predicate super T> var1)
public Optionalfilter(Predicate super T> var1) {Objects.requireNonNull(var1); if (!this.isPresent()) {return this; } else {return var1.test(this.value) ? this : empty(); }}
用于对optional对象的过滤,当optional包裹的值不为空时返回该值,否则执行filter入参的Predicate.test方法
举例
public static void testOptionalFilter() {Student student1 = getDefaultStudent(); Student student2 = getBackStudent(); System.out.println(student1); System.out.println(student2); Student student3 = Optional.ofNullable(student1).filter(i -> i.getAge() > 18).orElse(getBackStudent()); Student student4 = Optional.ofNullable(student2).filter(i -> i.getAge() > 18).orElse(getBackStudent()); System.out.println(student3); System.out.println(student4); }
实战
关于optional类的说明大致已经讲完,再回到开始的时候,提到的场景引入,结合optional进行改造
/** * 实战1 * 针对原来的checkIsPublicV1进行改造 */public static boolean checkIsPublicV2(Student student) {return Optional.ofNullable(student).map(i -> i.getSchool()).map(i -> i.isPublicFlag()).orElseThrow(() -> new RuntimeException("参数异常")); }/** * 实战1 * 针对原来的getAdultV1进行改造 */public static String getAdultV2(Student student) {return Optional.ofNullable(student).filter(i->i.getAge()>18).map(i->i.getName()).orElseGet(()->getDefaultStudent().getName()); }
附:
补充代码
public static void main(String[] args) {//逐个放开// 引入//System.out.println(checkIsPublicV1(stu2)); //System.out.println(getAdultV1(stu2)); // optional方法//testOptionalBuild(); //testOptionalOrElse(); //testOptionalIfPresent(); //testOptionalMap(); //testOptionalFilter(); // 实战//System.out.println(getAdultV2(stu3)); //System.out.println(checkIsPublicV2(stu3)); }/**========模型数据=======**/@Data@Builder@NoArgsConstructor@AllArgsConstructorstatic class Student {private String name; private int age; private School school; }@Data@Builder@NoArgsConstructor@AllArgsConstructorstatic class School {private String schoolName; private boolean publicFlag; }@Data@Builder@NoArgsConstructor@AllArgsConstructorstatic class StudentOpt {private String name; private int age; private Optional school; }public static Student getDefaultStudent() {return null; }public static Student getBackStudent() {return Student.builder().name("小红").age(19).build(); }public static Optional getOptionalStudent() {return Optional.ofNullable(StudentOpt.builder().name("小莫").age(18).school(Optional.ofNullable(School.builder().schoolName("蓝鲸大学").publicFlag(true).build())).build()); }public static void acceptStudent(Student stu, LocalDate date) {System.out.println("日期: " + date + " 新增一位学生: " + stu.getName()); }
【Java8中Optional类的使用说明】[1] 参考自java8实战
详细源码,请参考:github.com/chetwhy/clo…
总结 到此这篇关于Java8中Optional类使用的文章就介绍到这了,更多相关Java8 Optional类使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
推荐阅读
- 热闹中的孤独
- Shell-Bash变量与运算符
- JS中的各种宽高度定义及其应用
- 2021-02-17|2021-02-17 小儿按摩膻中穴-舒缓咳嗽
- 深入理解Go之generate
- 异地恋中,逐渐适应一个人到底意味着什么()
- 我眼中的佛系经纪人
- 《魔法科高中的劣等生》第26卷(Invasion篇)发售
- “成长”读书社群招募
- 2020-04-07vue中Axios的封装和API接口的管理