Java8中Optional类的使用说明

网友投稿 238 2022-11-26

Java8中Optional类的使用说明

目录简介历史null带来的种种问题方案场景引入方法说明构造函数创建Optional对象使用map从Optional对象中提取和转换值使用flatMap链接Optional对象默认行为及解引用Optional对象1默认行为及解引用Optional对象2使用filter剔除特定的值实战总结

简介

历史

1965年,英国一位名为Tony Hoare的计算机科学家在设计ALGOL W语言时提出了null引用的想法。Hoare选择null引用这种方式,“只是因为这种方法实现起来非常容易”。很多年后,他开始为自己曾经做过这样的决定而后悔不迭,把它称为“我价值百万的重大失误”。我们已经看到它带来的后果——程序员对对象的字段进行检查,判断它的值是否为期望的格式,最终却发现我们查看的并不是一个对象,而是一个空指针,它会立即抛出一个让人厌烦的NullPointerException异常[1]。

null带来的种种问题

错误之源。

NullPointerException是目前Java程序开发中最典型的异常。

代码膨胀。

http://

它让你的代码充斥着深度嵌套的null检查,代码的可读性糟糕透顶。

自身是毫无意义的。

null自身没有任何的语义,尤其是,它代表的是在静态类型语言中以一种错误的方式对缺失变量值的建模。

破坏了Java的哲学。

Java一直试图避免让程序员意识到指针的存在,唯一的例外是:null指针。

在Java的类型系统上开了个口子。

null并不属于任何类型,这意味着它可以被赋值给任意引用类型的变量。这会导致问题,原因是当这个变量被传递到系统中的另一个部分后,你将无法获知这个null变量最初的赋值到底是什么类型。

方案

汲取Haskell和Scala的灵感,Java 8中引入了一个新的类java.util.Optional。这是一个封装Optional值的类。举例来说,使用新的类意味着,如果你知道一个人可能有学校也可能没有,那么Student类内部的school变量就不应该声明为Schoold,遭遇某学生没有学校时把null引用赋值给它,而是应该像本篇那样直接将其声明为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();

}

}

rehttp://turn "无";

}

上述方式是我们常见的判读流程,optional就是针对每次空判断导致的代码欣赏性问题进行了一套解决方案

方法说明

构造函数

Optional(T var1)

源码

private final T value;

private Optional() {

this.value = 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 = null;

}

public static Optional empty() {

Optional var0 = EMPTY;

return var0;

}

从源码可知,optional类中维护了一个null值的对象,使用empty静态方法即可返回该空值对象

of(T var0)源码

public static Optional of(T var0) {

return new Optional(var0);

}

返回常见的有参构造对象,注意由于构造器要求入参不能为空,因此of方法的入参为空的话,依然会报NPE异常

ofNullable(T var0)源码

public static Optional 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 o2 = Optional.of(null);

System.out.println(o2);*/

// 3. ofNullable构建非空和空对象(Optional.empty)

/*Optional o3 = Optional.ofNullable("test");

System.out.println(o3);

Optional o4 = Optional.ofNullable(null);

System.out.println(o4);*/

}

使用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();

// map

String 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)

注:这三个方法方法不是静态方法,因此需要通过实例对象调用,一般跟在方法ofNullable后用于处理空值或返回值

源码

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 == null) {

throw new NoSuchElementException("No value present");

} else {

return this.value;

}

}

与上个方法类似,当optional中的包裹值不为空时返回包裹的值,若为空执行orElseGet中的Supplier方法

orElseThrow源码

public T 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() {

// orElse

Student stu = getDefaultStudent();

Student backStudent = getBackStudent();

Student realStu1 = Optional.ofNullable(stu).orElse(backStudent);

System.out.println(realStu1);

// orElseGet

Student realStu2 = Optional.ofNullable(stu).orElseGet(()-> getBackStudent());

System.out.println(realStu2);

// orElseGet

Student 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 Optional filter(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

@AllArgsConstructor

static class Student {

private String name;

private int age;

private School school;

}

@Data

@Builder

@NoArgsConstructor

@AllArgsConstructor

static class School {

private String schoolName;

private boolean publicFlag;

}

@Data

@Builder

@NoArgsConstructor

@AllArgsConstructor

static 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());

}

[1] 参考自java8实战

详细源码,请参考:github.com/chetwhy/clo…

总结

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:浅谈激光二极管和激光驱动器的特性
下一篇:Redis安装部署应用【篇】
相关文章

 发表评论

暂时没有评论,来抢沙发吧~