深入浅出讲解Optional包装类

深入浅出讲解Optional包装类Optional是JDK8的新特性,首先这个类是被final修饰的,并且这个类只有一个Object的超类,下面是这个类的结构。我们可以看到一共也没多少方法,所以我觉得就从这个类开始,养成阅读源码的习惯,再合适不过了。Optional的作用在写这篇博客之前,看过好多相关的资料,大体上来说这个类就是用来解决万恶的空指针异常,用来避免繁琐的!=null代码而存在的。那你也太…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

Optional是JDK8的新特性, 首先这个类是被final修饰的, 并且这个类只有一个Object的超类, 下面是这个类的结构。
在这里插入图片描述
我们可以看到一共也没多少方法, 所以我觉得就从这个类开始, 养成阅读源码的习惯, 再合适不过了。

Optional的作用

在写这篇博客之前, 看过好多相关的资料, 大体上来说这个类就是用来解决万恶的空指针异常, 用来避免繁琐的 !=null代码而存在的。 那你也太小看这个类了, 我总结看来, 这个类就是利用函数式编程来解决应对对象如果为空我们怎么办的问题, 本篇博客会详细的讲解源码中的每一个方法。 (注意函数式编程下面我会讲, 这段话听不懂也没有关系, 相信看完了这篇博客, 你会有对Optional类有一个比较深刻的理解)

Optional提供的方法

  • 创造方法
    /**
     * Returns an {@code Optional} with the specified present non-null value.
     *
     * @param <T> the class of the value
     * @param value the value to be present, which must be non-null
     * @return an {@code Optional} with the value present
     * @throws NullPointerException if value is null
     */
    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

    /**
     * Returns an {@code Optional} describing the specified value, if non-null,
     * otherwise returns an empty {@code Optional}.
     *
     * @param <T> the class of the value
     * @param value the possibly-null value to describe
     * @return an {@code Optional} with a present value if the specified value
     * is non-null, otherwise an empty {@code Optional}
     */
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

我们可以看到源码中的构造函数全部都私有化了, 我们只能够通过工厂方法的方式来获取Optional包装类的实例。
那么这两种方式有什么不同呢? 我们看方法名其实就能猜到, 上面的方法是不允许传null值的, 下面的是可以的。如果上面的方法传了null值private Optional(T value) { this.value = Objects.requireNonNull(value); }调用这个构造函数的时候就会抛出万恶的空指针。下面的方法传了空值, 它会默认的帮你创建一个空的Optional包装类对象。
测试代码如下:

/**
     * 测试Optional包装类的创建方式, 两种创建方式
     * 
     * @see java.util.Optional#of(Object)
     * @see java.util.Optional#ofNullable(Object)
     */
    @Test
    public void testCreate() {
        // create one
        Optional<User> userOne = Optional.<User>ofNullable(new User());

        // 获取create one中封装的对象
        if (userOne.isPresent()) {
            Assert.assertNotNull(userOne.get());
        }
    }

其中 isPresent() 是判断这个包装类是否为空, get() 是获取到被包装的对象。

  • 如果不为空
    我们看一下源码:
    /**
     * If a value is present, invoke the specified consumer with the value,
     * otherwise do nothing.
     *
     * @param consumer block to be executed if a value is present
     * @throws NullPointerException if value is present and {@code consumer} is
     * null
     */
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

这个代码非常简洁, 只是传了一个Consumer的接口, 那么这个接口是什么呢? 这就是著名的函数式编程。 点开这个接口的源码我们会发现这个接口被 @FunctionalInterface注解修饰了, 这就是告诉你, 这是一个函数式编程的接口, 这类的接口有且只有一个待实现的抽象方法, 可以使用lamda表达式。我们看一下 java.util.function包下, 这类的接口可是不少。
在这里插入图片描述
我们再看一下Consumer接口的源码

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

我们在调用ifPresent方法的时候会把被包装的对象当做参数传进来做一些操作, 这就要求我们调用的时候要用lamda表达式实现这个接口。

/**
     * 测试Optional类的ifPresent方法
     * 
     * @see java.util.Optional#ifPresent(java.util.function.Consumer)
     */
    @Test
    public void testIfPresent() {

        // create one
        Optional<User> userOne = Optional.<User>ofNullable(new User());
        // 用LAMDA表达式实现Consumer接口
        userOne.ifPresent(e -> e.setEmial("yanghang@163.com"));
        // test
        System.out.println(userOne.get().getEmial());
    }

我们可以这么理解, 调用的时候我们把封装的user对象当成e来看, 那么这段代码运行结束, user对象里面的email属性不就有值了?
所以这个方法就是, 如果你的包装类不为空, 那么我就调用Consumer的实现来对你被封装的对象做带你什么。

  • 如果怎么样, 我就给你
/**
     * If a value is present, and the value matches the given predicate,
     * return an {@code Optional} describing the value, otherwise return an
     * empty {@code Optional}.
     *
     * @param predicate a predicate to apply to the value, if present
     * @return an {@code Optional} describing the value of this {@code Optional}
     * if a value is present and the value matches the given predicate,
     * otherwise an empty {@code Optional}
     * @throws NullPointerException if the predicate is null
     */
    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

这也是一个函数式编程的接口, Predicate接口要求你返回一个boolean类型的值, 如果是true, 我就给你引用, 如果是flase我就给你空的包装类Optional。

    /**
     * 测试Optional类的filter方法
     * 
     * @see java.util.Optional#filter(java.util.function.Predicate)
     */
    @Test
    public void testFilter() {
        // create one
        Optional<User> userOne = Optional.<User>ofNullable(new User());
        // 用LAMDA表达式实现Predicate接口
        userOne = userOne.filter(e -> {
            return e.getName() == null || "".equals(e.getName());
        });
        // test
        Assert.assertTrue(userOne.isPresent());
    }
  • 你给了我, 我却不想要你
    /**
     * If a value is present, apply the provided mapping function to it,
     * and if the result is non-null, return an {@code Optional} describing the
     * result.  Otherwise return an empty {@code Optional}.
     *
     * @apiNote This method supports post-processing on optional values, without
     * the need to explicitly check for a return status.  For example, the
     * following code traverses a stream of file names, selects one that has
     * not yet been processed, and then opens that file, returning an
     * {@code Optional<FileInputStream>}:
     *
     * <pre>{@code
     *     Optional<FileInputStream> fis =
     *         names.stream().filter(name -> !isProcessedYet(name))
     *                       .findFirst()
     *                       .map(name -> new FileInputStream(name));
     * }</pre>
     *
     * Here, {@code findFirst} returns an {@code Optional<String>}, and then
     * {@code map} returns an {@code Optional<FileInputStream>} for the desired
     * file if one exists.
     *
     * @param <U> The type of the result of the mapping function
     * @param mapper a mapping function to apply to the value, if present
     * @return an {@code Optional} describing the result of applying a mapping
     * function to the value of this {@code Optional}, if a value is present,
     * otherwise an empty {@code Optional}
     * @throws NullPointerException if the mapping function is null
     */
    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));
        }
    }

    /**
     * If a value is present, apply the provided {@code Optional}-bearing
     * mapping function to it, return that result, otherwise return an empty
     * {@code Optional}.  This method is similar to {@link #map(Function)},
     * but the provided mapper is one whose result is already an {@code Optional},
     * and if invoked, {@code flatMap} does not wrap it with an additional
     * {@code Optional}.
     *
     * @param <U> The type parameter to the {@code Optional} returned by
     * @param mapper a mapping function to apply to the value, if present
     *           the mapping function
     * @return the result of applying an {@code Optional}-bearing mapping
     * function to the value of this {@code Optional}, if a value is present,
     * otherwise an empty {@code Optional}
     * @throws NullPointerException if the mapping function is null or returns
     * a null result
     */
    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));
        }
    }

同样, 也是函数式编程的接口Function, 这两个类就是你给了我小猫的引用, 我还给你一个小狗的对象。不同的就是一个会帮你自动包装成包装类Optional, 一个需要你手动。

    /**
     * 测试Optional类的map方法和flatMap方法
     * 
     * @see java.util.Optional#map(java.util.function.Function)
     * @see java.util.Optional#flatMap(java.util.function.Function)
     */
    @Test
    public void testMapAndFlatMap() {
        // create one
        User user = new User("yanghang@163.com");
        Optional<User> userOne = Optional.<User>ofNullable(user);
        // 用LAMDA表达式实现Function接口
        Optional<Person> personOne = userOne.map(e -> {
            Person per = new Person();
            per.setEmial(e.getEmial());
            return per;
        });
        // test
        System.out.println(personOne.get().getEmial());
    }
  • 如果你是null, 我就
 /**
     * Return the value if present, otherwise return {@code other}.
     *
     * @param other the value to be returned if there is no value present, may
     * be null
     * @return the value, if present, otherwise {@code other}
     */
    public T orElse(T other) {
        return value != null ? value : other;
    }

    /**
     * Return the value if present, otherwise invoke {@code other} and return
     * the result of that invocation.
     *
     * @param other a {@code Supplier} whose result is returned if no value
     * is present
     * @return the value if present otherwise the result of {@code other.get()}
     * @throws NullPointerException if value is not present and {@code other} is
     * null
     */
    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }

    /**
     * Return the contained value, if present, otherwise throw an exception
     * to be created by the provided supplier.
     *
     * @apiNote A method reference to the exception constructor with an empty
     * argument list can be used as the supplier. For example,
     * {@code IllegalStateException::new}
     *
     * @param <X> Type of the exception to be thrown
     * @param exceptionSupplier The supplier which will return the exception to
     * be thrown
     * @return the present value
     * @throws X if there is no value present
     * @throws NullPointerException if no value is present and
     * {@code exceptionSupplier} is null
     */
    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

这三个方法大致上相似, 都是如果被包装类为空, 我就怎样, 一个是直接返回一个新的被包装对象, 一个是通过函数式编程接口Supplier返回一个新的被包装类对象, 最后一个更狠, 我直接给你返回一个异常。

/**
     * 测试Optional类的orElse方法
     * 
     * @see java.util.Optional#orElse(Object)
     */
    @Test
    public void testOrElse() {
        Optional<User> userOne = Optional.<User>ofNullable(null);
        // 用LAMDA表达式实现Function接口
        User user = userOne.orElse(new User("anqichong@163.com"));
        // test
        System.out.println(user.getEmial());
    }

好了, Optional的源码就这么多, 如果大家看不懂的话, 建议先去看一下lamda表达式的内容, 一定会有多收获的。我叫杨小邪, 现在很晚了。。。。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/191162.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号