概念

Optional 是JDK1.8中出现的一个容器类,代表一个值存在或者不存在。原来使用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。

场景分析

需求:假如我们要取一个男人心中的女神名字

假如不使用Optional来实现

男人Man.java

public class Man {

    private Goddess goddess;

    public Goddess getGoddess() {
return goddess;
} public void setGoddess(Goddess goddess) {
this.goddess = goddess;
}
}

女神Goddess.java

public class Goddess {

    private String name;

    public Goddess(String name) {
this.name = name;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}

测试类Main.java

public class Main {

    public static void main(String[] args) {
Man man = new Man();
System.out.println(getGoddessOfMan(man));
} // 获取男人心中的女神
public static String getGoddessOfMan(Man man) {
return man.getGoddess().getName();
}
}

出现异常:

如果单看报错内容,我们可以知道是man.getGoddess().getName();这条语句发生了空指针异常,但是我们还需要进一步定位才能知道究竟是man为null,还是goddess为null

我们可以改写测试类代码来避免这个异常

// 获取男人心中的女神
public static String getGoddessOfMan(Man man) {
if (man == null || man.getGoddess() == null) {
return "";
}
return man.getGoddess().getName();
}

假如用上Optional来实现

改写Man.java

public class Man {

    private Optional<Goddess> goddess = Optional.empty();

    public Optional<Goddess> getGoddess() {
return goddess;
} public void setGoddess(Optional<Goddess> goddess) {
this.goddess = goddess;
}
}

Goddess.java不改写

改写测试类Main.java

public class Main {

    public static void main(String[] args) {
Man man = new Man();
System.out.println(getGoddessOfMan(Optional.ofNullable(man)));
man = null;
System.out.println(getGoddessOfMan(Optional.ofNullable(man)));
} // 获取男人心中的女神
public static String getGoddessOfMan(Optional<Man> optionalMan) {
return optionalMan.orElse(new Man()).getGoddess().orElse(new Goddess("蒙娜丽莎")).getName();
}
}

控制台中打印出

蒙娜丽莎

蒙娜丽莎

其实,看上去也没有省几行代码嘛?那么我们接着往下分析Optional类。

分析Optional容器类

1.Optional的创建方法

Optional的核心且唯一的属性就是T value

另外,因为Optional的构造器都被私有化了,所以只能通过静态方法创建Optional对象。

1.1 静态创建方法of(T t) --- 不允许参数为null

测试代码如下,我们尝试传一个参数nullof(T t)方法,结果发生了NullPointerException

// of方法会判断参数是否为null,如果为null,会报空指针异常
Optional<Goddess> op = Optional.of(null);

我们深入java.util.Optional的源代码

public static <T> Optional<T> of(T value) {
return new Optional<>(value);
} private Optional(T value) {
this.value = Objects.requireNonNull(value);
}

再追踪到java.util.Objects的源代码

public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}

1.2 静态创建方法empty() --- 创建一个value=null的Optional容器对象

再看java.util.Optionalempty(),返回一个成员变量value为null的Optional容器对象

private static final Optional<?> EMPTY = new Optional<>();

public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}

1.3 静态创建方法ofNullable(T t) --- 允许参数为null

仍然看java.util.Optional的源代码,ofNullable表示可以接受null,并使用empty()返回。也接受参数value不为null,使用of(T t)返回。

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

2.Optional的判断和获取 --- 先使用isPresent()判断,再使用get()获取

测试代码如下

import java.util.Optional;

public class OptionalTest {

    public static void main(String[] args) {
Optional<Goddess> op = Optional.of(new Goddess("蒙娜丽莎"));
if (op.isPresent()) {
System.out.println(op.get().getName());
} Optional<Goddess> empty = Optional.ofNullable(null);
// 先通过isPresent()判断,再使用get()来避免直接使用empty.get().getName()可能带来NoSuchElementException异常
// if (empty.isPresent()) {
System.out.println(empty.get().getName());
// }
}
}

控制台输出如下

蒙娜丽莎

Exception in thread "main" java.util.NoSuchElementException: No value present

at java.util.Optional.get(Optional.java:135)

at optional.OptionalTest.main(OptionalTest.java:15)

从控制台输出我们可以知道,在使用get()方法之前,最好先用isPresent()判断Optional中的成员变量value值是否存在。

3.把判断代码放在Optional类内的方法

3.2 orElse

方法 描述
T orElse(T other) 表示如果调用该方法的Optional对象的成员变量value不为null则返回value,否则返回other
T orElseGet(Supplier<T> supplier) 表示如果调用该方法的Optional对象的成员变量value不为null则返回value,否则用Supplier生成一个用于返回的T对象
T orElseThrow(Supplier<? extends X> exceptionSupplier) 表示如果调用该方法的Optional对象的成员变量value不为null则返回value,否则用Supplier生成一个用于抛出的异常对象

我们看可以查看一下java.util.Optional源码

public T orElse(T other) {
return value != null ? value : other;
} public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
} public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}

3.3 剩下的成员方法

方法 如果参数为null 如果成员变量valuenull 如果成员变量value不为null
Optional<T> filter(Predicate<? super T> predicate) 抛出NullPointerException 返回empty() 如果predicate.test(T t)true,返回当前对象,否则返回empty()
Optional<U> map(Function<? super T, ? extends U> mapper) 抛出NullPointerException 返回empty() 转换T为U,再返回Optional.ofNullable(U)
Optional<U> flatMap(Function<? super T, Optional<U>> mapper) 抛出NullPointerException 返回empty() 转换T为Optional<U>,转换后的对象如果为null,抛出NullPointerException

测试代码

import java.util.Optional;

public class OptionalTest {

    public static void main(String[] args) {
Optional<Goddess> op = Optional.of(new Goddess("蒙娜丽莎"));
Optional<Goddess> nullOp = Optional.ofNullable(null);
// 如果女神名称不为null,filter返回op,否则返回empty()
System.out.println(op.filter(goddess->goddess.getName() != null).isPresent());
System.out.println(nullOp.filter(goddess->goddess.getName() != null).isPresent());
// 映射返回女神名称
System.out.println(op.map(Goddess::getName).get());
System.out.println(nullOp.map(Goddess::getName).isPresent());
// 映射返回装有女神名称的Optional容器对象
System.out.println(op.flatMap(goddess->Optional.ofNullable(goddess.getName())).get());
System.out.println(nullOp.flatMap(goddess->Optional.ofNullable(goddess.getName())).isPresent());
}
}

控制台输出如下:

true

false

蒙娜丽莎

false

蒙娜丽莎

false

再来看一下java.util.Optional源码

public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
} public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
} public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}

结语:回到最初的需求

假如我们扩展一下我们的需求

定义一个方法,获取男人心中的女神,要求男人不存在时抛出异常,如果男人存在,但是这个男人没有女神,那么给一个默认的女神名字-蒙娜丽莎

import java.util.Optional;

public class GetGoddessOfMan {

    public static void main(String[] args) {
Man man = new Man();
System.out.println(getGoddessOfMan(Optional.ofNullable(man)));
man = null;
System.out.println(getGoddessOfMan(Optional.ofNullable(man)));
} // 获取男人心中的女神
public static String getGoddessOfMan(Optional<Man> optionalMan) {
return optionalMan.orElseThrow(NullPointerException::new).getGoddess().orElse(new Goddess("蒙娜丽莎")).getName();
}
}

这里就比较简洁了,一句话就反映出了需求,且更接近自然语言。如果要用非Optional实现,代码类似下面这种

// 获取男人心中的女神
public static String getGoddessOfMan(Man man) {
Objects.requireNonNull(man);
Goddess goddess = man.getGoddess() == null ? new Goddess("蒙娜丽莎") : man.getGoddess();
return goddess.getName();
}

不过很多小伙伴还是会觉得这个写法看上去有些奇怪。在其他JDK1.8的新特性中,比如Stream流中也有返回Optional<T>的函数,比如Optional<T> findFirst();,Optional<T> findAny();,Optional<T> max(Comparator<? super T> comparator), Optional<T> min(Comparator<? super T> comparator);等等

JDK1.8新特性之Optional的更多相关文章

  1. JDK1.8新特性——Optional类

    JDK1.8新特性——Optional类 摘要:本文主要学习了JDK1.8新增加的Optional类. 部分内容来自以下博客: https://www.cnblogs.com/1ning/p/9140 ...

  2. JDK1.8新特性——Collector接口和Collectors工具类

    JDK1.8新特性——Collector接口和Collectors工具类 摘要:本文主要学习了在Java1.8中新增的Collector接口和Collectors工具类,以及使用它们在处理集合时的改进 ...

  3. JDK1.8新特性——Stream API

    JDK1.8新特性——Stream API 摘要:本文主要学习了JDK1.8的新特性中有关Stream API的使用. 部分内容来自以下博客: https://blog.csdn.net/icarus ...

  4. JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用

    jdk1.8新特性知识点: Lambda表达式 Stream API 函数式接口 方法引用和构造器调用 接口中的默认方法和静态方法 新时间日期API default   Lambda表达式     L ...

  5. JDK1.8新特性之Stream类初识

    JDK1.8新特性之Stream类初识 import java.util.Arrays; import java.util.List; import java.util.Optional; impor ...

  6. JDK1.8新特性之(三)--函数式接口

    在上一篇文章中我们介绍了JDK1.8的新特性有以下几项. 1.Lambda表达式 2.方法引用 3.函数式接口 4.默认方法 5.Stream 6.Optional类 7.Nashorm javasc ...

  7. JDK1.8新特性之(一)--Lambda表达式

    近期由于新冠疫情的原因,不能出去游玩,只能在家呆着.于是闲来无事,开始阅读JDK1.8的源代码.在开始之前也查询了以下JDK1.8的新特性,有针对性的开始了这段旅程. 只看不操作,也是不能心领神会的. ...

  8. JDK1.8新特性之(二)--方法引用

    在上一篇文章中我们介绍了JDK1.8的新特性有以下几项. 1.Lambda表达式 2.方法引用 3.函数式接口 4.默认方法 5.Stream 6.Optional类 7.Nashorm javasc ...

  9. JDK1.7新特性

    jdk1.7新特性 1 对集合类的语言支持: 2 自动资源管理: 3 改进的通用实例创建类型推断: 4 数字字面量下划线支持: 5 switch中使用string: 6 二进制字面量: 7 简化可变参 ...

随机推荐

  1. C指针乱记

    //int a[3][4] = { { 66, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }; //读取二维数组任意元素hint int(*)a[4] ...

  2. C# MVC中直接执行Js

    .NET MVC里如何在服务器端执行JS: 三种解决方案: 1.直接返回JavaScript. public ActionResult XXXAction1() {      return JavaS ...

  3. 10 | MySQL为什么有时候会选错索引?

    前面我们介绍过索引,你已经知道了在MySQL中一张表其实是可以支持多个索引的.但是,你写SQL语句的时候,并没有主动指定使用哪个索引.也就是说,使用哪个索引是由MySQL来确定的. 不知道你有没有碰到 ...

  4. STS工具:mybayis连接oracle数据库

    1.pom.xml文件中的依赖 刚添加依赖的时候会报错,原因是jar包下不下来. 2.我的jdk是1.6,所以需要升级jdk版本到1.8 执行mvn -v命令,可以看到maven的版本号 DOS窗口执 ...

  5. locale与C字符编码

    ref: https://www.cnblogs.com/gatsby123/p/11150472.html Unicode 字符集 代码点 与编码表中的某个字符对应的代码值.在Unicode标准中, ...

  6. 普转提——有趣的数,欢乐ABC,打游戏

    有趣的数——构造符合条件的数 给你一个区间,问有多少个数符合每一位中,只有一个数字和其他数字不同,也就是其他数字都相同,有且只有一个异类: 数据范围是1e16: 因为只考虑数量而不用管大小: 只要0到 ...

  7. 利用Python爬取朋友圈数据,爬到你开始怀疑人生

    人生最难的事是自我认知,用Python爬取朋友圈数据,让我们重新审视自己,审视我们周围的圈子. 文:朱元禄(@数据分析-jacky) 哲学的两大问题:1.我是谁?2.我们从哪里来? 本文 jacky试 ...

  8. 使用Excel拼凑SQL语句

       快速将一列多行数据合并到一个单元格            EXCEL如何快速将一列多行数据合并到一个单元格,并加分隔符?这是批量处理由一线业务员统计的数据时的常用方法,尤其是当一列数据是wher ...

  9. UVALive 4976 Defense Lines ——(LIS变形)

    题意:给出序列,能够从这序列中删去连续的一段,问剩下的序列中的最长的严格上升子串的长度是多少. 这题颇有点LIS的味道.因为具体做法就是维护一个单调的集合,然后xjbg一下即可.具体的见代码吧: #i ...

  10. android中的Section ListView

    前几天,和ios开发的同事扯淡时发现iphone里有个section listview,分章节的列表.android中的联系人也有这种效果,首字母相同的联系人会被分在一个章节中. 后来搜了一下,and ...