JDK1.8新特性之Optional

概念

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);等等

原文地址:https://www.cnblogs.com/zaid/p/12001384.html

时间: 2024-08-30 15:29:21

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

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

JDK1.8新特性——Collector接口和Collectors工具类 摘要:本文主要学习了在Java1.8中新增的Collector接口和Collectors工具类,以及使用它们在处理集合时的改进和优化. 部分内容来自以下博客: https://www.jianshu.com/p/7eaa0969b424 流式处理 JDK1.8中新增的流式处理提供了一种高效且易于使用的处理数据的方式,它可以对集合执行非常复杂的查找.过滤和映射数据等操作,极大的简化了对于集合的使用.借助流式处理,可以像使用S

jdk1.5新特性和jdk1.7新特性

jdk1.5新特性 1.自动装箱和自动拆箱 自动装箱,把基本类型的值变成对象类型 Integer a = 2; 自动拆箱,把对象类型变成基本类型 int b = new Integer(2); 而不需要调用intValue int b = new Integer(2).intValue(); 包装类有 Integer Boolean Float Double Short Byte 2.枚举 java枚举具有简单和安全性. 3.泛型 泛型在编译的时候可以控制类型,比如说List<String>

黑马程序员——java高新---JDK1.5新特性和反射

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 一.JDK1.5新特性 ——>静态导入 import和import static之间的区别: 1.import是导入一个类或某个包中所有的类. 2.import static是导入一个类中的某个静态成员或所有的静态成员. 注意: 1.当导入的两个类中有同名成员时,需要在成员前加上相应的类名. 2.当类名重名时,需要指定具体的包名. 3.方法重名时,需要指定具体所属的对象或者类. 代码示例: 1

接口、接口优点、接口的属性和方法特性、接口与继承的区别、接口与抽象类的区别、匿名实现类、JDK1.8新特性、打印类名称

接口里的属性,必须是公共的/默认的&静态的&Final&初始化后的属性: 接口里所有方法都是抽象的. 接口和继承---先天有的用继承,后天学习的用接口: 接口特性-------- 1.接口不可以被实例化: 2.实现类必须实现接口中所有方法,否则实现类必须是抽象类 3.实现类可以实现多个接口,来弥补Java不能多继承: 4.接口中的变量都是静态常量:(静态的-可以通过类的名字点-直接调用:) ----------------------------------------------

jdk1.5新特性

package cn.itcast.jdk15; import java.util.ArrayList; /* jdk1.5新特性之-----自动装箱与自动拆箱. java是面向对象 的语言,任何事物都可以使用类进行描述,sun就使用了 一些类描述java中八种基本数据类型数据 基本数据类型 包装类型 byte Byte short Short int Integer long Long float Float double Double boolean Boolean char Charact

黑马程序员------Java中jdk1.5新特性

Java培训.Android培训.iOS培训..Net培训.期待与您交流! JDK1.5新特性: 为什么会出现新特性: 新的技术出现是为了解决老的问题,Java语言为了提高开发者的开发效率,对之前的某些不利于提高效率的技术进行改进. 静态导入: 静态导入:可以导入某个类下的静态方法,静态导入后,可以不写类名而直接使用此类下的静态方法. 语法:import static 包名.类名.静态方法 代码示例: package com.itheima.day1; /** * 静态导入 * @author

jdk1.5新特性之-------&gt;可变参数

/* jdk1.5新特性之------->可变参数 需求: 定义一个函数做加法功能(函数做几个数据 的加法功能是不确定). 可变参数的格式: 数据类型... 变量名 可变参数要 注意的细节: 1. 如果一个函数 的形参使用上了可变参数之后,那么调用该方法的时候可以传递参数也可以不传递参数. 2. 可变参数实际上是一个数组对象. 3. 可变参数必须位于形参中的最后一个参数. 4. 一个函数最多只能有一个可变 参数,因为可变参数要位于形参中最后一个位置上. */ public class Demo4

Java 8 新特性:Java 类库的新特性之 Optional类 ——诺诺&quot;涂鸦&quot;记忆

----------   诺诺学习技术交流博客.期待与您交流!    ---------- 详情请查看:http://blog.csdn.net/sun_promise  Java 类库的新特性之 Optional类 1.Optional简述 到目前为止,著名的NullPointerException是导致Java应用程序失败的最常见原因.过去,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的

59. jdk1.5新特性之----增强for循环

/*jdk1.5新特性之----增强for循环:底层是一个迭代器 作用:简化迭代器书写格式 使用范围:实现了Iterable接口的对象或者数组对象 格式:    for(变量类型  变量名 :遍历目标){        //代码块    }    注意:    1.因为for in循环底层还是迭代器,所以在循环的时候我们不能修改迭代对象的长度    2.因为我们没有实例化迭代器对象,所以我们不能使用迭代器中的一些方法*/ 基本运用实例代码: public class Demo2 { public