Java 知识点整理 Optional 的使用

2020/1/30 17:11:34

本文主要是介绍Java 知识点整理 Optional 的使用,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

前言

开发相关知识点整理,内容来自我的个人网站笔记,和收集参考的资料,由于是发在社区,整理了排版和可读性,对于内容我尽量做到是经过验证的,以免误人子弟。当然本人能力有限,如有错误或疑问,欢迎一起讨论和指正(有些内容也是我参考别人的,如有侵权,请联系我,一般我会注明转载和参考原文)

1、什么是Optional?

一句话概括: 它是一个容器,用来包装对象,解决NullPointerException异常的

2、如何创建Optional对象?或者说如何用Optional来包装对象或null值?

  • static Optional empty() :用来创建一个空的Optional

  • static Optional of(T value) :用来创建一个非空的Optional

  • static Optional ofNullable(T value) :用来创建一个可能是空,也可能非空的Optional

其实上面这三个方法,看一下源码就很清晰了,比如of

// 方法
public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }
// 构造器
private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }
// Objects对象的requireNonNull方法
public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }
复制代码

of方法创建Optional对象,并对传入值做NullPointerException异常判断,所以,当你传递一个null值的时候,就会触发异常了

再看看empty方法

// 方法
public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
// 静态常量
private static final Optional<?> EMPTY = new Optional<>();

// 常量
private final T value;

// 构造器
private Optional() {
    this.value = null;
    }
复制代码

一开始就定义了一个Optional<?> EMPTY的对象,并且构造函数使用默认的,value为空。empty只是做了泛型的转换

剩下的ofNullable就更简单了,通过传递的值决定使用of还是empty

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

Optional类的源码还是比较简单的,代码很少,通过成员变量和构造方法的解读,相信你已经理解了of,empty,ofNullable

3、如何使用Optional类?

最正确的做法就是对需要做NullPointerException异常判断的对象,把它包装成Optional类,然后指明对象不存在的时候,应该怎么做,下面来看一下几种常见的用法

1. orElse

public class OptionalTest {
    public static void main(String[] args) {
        HashMap<Integer, User> userHashMap = new HashMap<>();
        userHashMap.put(1, new User(1, "Xiao Ming"));
        userHashMap.put(2, new User(2, "Xiao Zhi"));
        userHashMap.put(3, null);

        UserUtil userUtil = new UserUtil(userHashMap); // 这个工具类只是为了填充数据的,不要在意这些细节,getUserByUserId方法能返回User对象

        // 包装了User对象,并且使用orElse指明了对象不存在的时候应该返回指定的值,也就是new User(1, "Xiao Bai")
        User user = Optional
                .ofNullable(UserUtil.getUserByUserId(2))
                .orElse(new User(1, "Xiao Bai"));

    }
}

// 工具类,随便写的,通过hashMap模拟查询用户
public class User {
    public Integer id;
    public String name;

    public User(Integer id, String name) {
        this.id = id;
        this.name = name;
    }
}

class UserUtil {

    public static HashMap<Integer, User> hashMap;

    UserUtil(HashMap<Integer, User> hashMap) {
        UserUtil.hashMap = hashMap;
    }

    public static User getUserByUserId(Integer id) {
        User user = hashMap.get(id);
        System.out.println(user);
        return user;
    }
}
复制代码

2. orElseGet

和 orElse 不同,它的参数接受一个lambda表达式

User user = Optional
                .ofNullable(UserUtil.getUserByUserId(2))
                .orElseGet(() -> new User(1, "Xiao Bai"));
复制代码

3. orElseThrow

同理,传递一个lambda表达式异常(注意啊,方法是限定了参数的,触发异常就用orElseThrow,不能用orElseGet)

User user = Optional
                .ofNullable(UserUtil.getUserByUserId(2))
                .orElseThrow(()-> new AssertionError("AssertionError"));
复制代码

4. map

调用map后,如果当前 Optional 为 Optional.empty,则依旧返回 Optional.empty;否则返回一个新的 Optional,该 Optional 包含的是:函数 mapper 在以 value 作为输入时的输出值

比如下面的例子,第一次调用map后,获取的是name,传递给下一个map的值相当于Optional.ofNullable(name)

String user = Optional.ofNullable(UserUtil.getUserByUserId(1))
                .map(user1 -> user1.name)
                .map(String::toLowerCase)
                .orElse("123");
        System.out.println(user);

// 方法
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));
        }
    }
复制代码

这个操作可以用在对对象做多次操作的场景下,并且保证为空的时候返回指定的值,比如先获取用户的名称,再获取用户的电话,做一下判断后,再通过电话查询到其它的数据,然后继续...,最后如果某一环节出现异常,那就返回orElse定义的对象

5. flatMap

flatMap 方法与 map 方法的区别在于,map 方法参数中的函数 mapper 输出的是值,然后 map 方法会使用 Optional.ofNullable 将其包装为 Optional;而 flatMap 要求参数中的函数 mapper 输出的就是 Optional

.flatMap(user -> Optional.of(user.name()))

6. filter

都是差不多的套路,这次我们先看源码,可以发现同样是接受lambda表达式,并且要是一个Boolean返回值的,如果本次操作的optional是empty的,就返回本身

public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

// 函数式接口部分代码,可以看到test是要求返回Boolean类型的

@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
}

// 例子
String name = Optional.ofNullable(UserUtil.getUserByUserId(1))
                .filter(user -> user.id == 2)
                .map(user -> user.name)
                .orElse("abc");
        System.out.println(name);
复制代码

通过观察源码,我们可以看到,很多为空的都会返回empty,这让各个操作都能够互相调用

总结

Optional是一个比较简单的类,推荐直接阅读源码,通过简单的包装,很优雅的解决的空指针问题,以前谷歌Guava库就实现了,后面Java8正式把规范加到 java.util.Optional 类中。

另外,上面的做法是对于程序内的,如果是web开发,参数校验,请使用Hibernate-Validator即可,作为一个合格的后端,我不会让前端挑刺的😀



这篇关于Java 知识点整理 Optional 的使用的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程