深入浅出讲解Optional包装类
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)*/@Testpublic void testCreate() {// create oneOptional<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)*/@Testpublic void testIfPresent() {// create oneOptional<User> userOne = Optional.<User>ofNullable(new User());// 用LAMDA表达式实现Consumer接口userOne.ifPresent(e -> e.setEmial("yanghang@163.com"));// testSystem.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;elsereturn predicate.test(value) ? this : empty();}
这也是一个函数式编程的接口, Predicate接口要求你返回一个boolean类型的值, 如果是true, 我就给你引用, 如果是flase我就给你空的包装类Optional。
/*** 测试Optional类的filter方法* * @see java.util.Optional#filter(java.util.function.Predicate)*/@Testpublic void testFilter() {// create oneOptional<User> userOne = Optional.<User>ofNullable(new User());// 用LAMDA表达式实现Predicate接口userOne = userOne.filter(e -> {return e.getName() == null || "".equals(e.getName());});// testAssert.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)*/@Testpublic void testMapAndFlatMap() {// create oneUser 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;});// testSystem.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)*/@Testpublic void testOrElse() {Optional<User> userOne = Optional.<User>ofNullable(null);// 用LAMDA表达式实现Function接口User user = userOne.orElse(new User("anqichong@163.com"));// testSystem.out.println(user.getEmial());}
好了, Optional的源码就这么多, 如果大家看不懂的话, 建议先去看一下lamda表达式的内容, 一定会有多收获的。我叫杨小邪, 现在很晚了。。。。
深入浅出讲解Optional包装类相关推荐
- 深入浅出讲解语言模型
转载自 深入浅出讲解语言模型 深入浅出讲解语言模型 1.什么是语言模型呢? 简单地说,语言模型就是用来计算一个句子的概率的模型,也就是判断一句话是否是人话的概率? 那么如何计算一个句子的概率呢?给定 ...
- 深入浅出讲解LDA主题模型(一)
本文转自:深入浅出讲解LDA主题模型(一)_欢迎来到我的酒馆-CSDN博客 原文格式更适合阅读,本文主要是为了备忘. 最近总是遇到主题模型LDA(Latent Dirichlet Allocation ...
- 深入浅出讲解FOC控制与SVPWM技术
深入浅出讲解FOC控制与SVPWM技术 0.前言 0.1 什么是FOC 0.2 FOC驱动器和无刷电调的区别 1.从电机原理说起 1.1 一些基础知识 1.2 无刷电机原理 1.3 关于BLDC和PM ...
- 【seq2seq】深入浅出讲解seq2seq神经网络模型
本文收录于<深入浅出讲解自然语言处理>专栏,此专栏聚焦于自然语言处理领域的各大经典算法,将持续更新,欢迎大家订阅! 个人主页:有梦想的程序星空 个人介绍:小编是人工智能领域硕士,全栈工程师 ...
- 【知识图谱】深入浅出讲解知识图谱(技术、构建、应用)
本文收录于<深入浅出讲解自然语言处理>专栏,此专栏聚焦于自然语言处理领域的各大经典算法,将持续更新,欢迎大家订阅! 个人主页:有梦想的程序星空 个人介绍:小编是人工智能领域硕士,全栈工程师 ...
- 【朴素贝叶斯】深入浅出讲解朴素贝叶斯算法(公式、原理)
本文收录于<深入浅出讲解自然语言处理>专栏,此专栏聚焦于自然语言处理领域的各大经典算法,将持续更新,欢迎大家订阅! 个人主页:有梦想的程序星空 个人介绍:小编是人工智能领域硕士,全栈工 ...
- 【LSTM】深入浅出讲解长短时记忆神经网络(结构、原理)
本文收录于<深入浅出讲解自然语言处理>专栏,此专栏聚焦于自然语言处理领域的各大经典算法,将持续更新,欢迎大家订阅! 个人主页:有梦想的程序星空 个人介绍:小编是人工智能领域硕士,全 ...
- 转稚晖军大佬 --【自制FOC驱动器】深入浅出讲解FOC算法与SVPWM技术
转稚晖军大佬 --[自制FOC驱动器]深入浅出讲解FOC算法与SVPWM技术 [自制FOC驱动器]深入浅出讲解FOC算法与SVPWM技术 0.前言 最近想做一个机器人项目,设计中需要用到高性能超小体积 ...
- 深入浅出讲解神经网络的种类及举例说明
本文收录于<深入浅出讲解自然语言处理>专栏,此专栏聚焦于自然语言处理领域的各大经典算法,将持续更新,欢迎大家订阅! 个人主页:有梦想的程序星空 个人介绍:小编是人工智能领域硕士,全栈工程师 ...
最新文章
- 基于自动驾驶车辆的激光雷达与摄像头之间的在线标定算法
- java缩放浏览器_javascript检测浏览器的缩放状态实现代码
- lable标签的妙用
- python(1):数据类型/string/list/dict/set等
- 从物理服务器拷贝文件到容器,docker容器与物理机的文件传输—docker cp命令
- keeplive linux平台下,Linux下搭建keepalive+nginx
- etl工程师 面试题_数据仓库工程师面试题笔试.doc
- Java LinkedList公共布尔boolean offerFirst(Object o)方法(带示例)
- 阿里P7三面被这10个SpringCloud微服务问题难倒,杯具!
- 数据集:两种形式的铁离子在不同剂量下在动物体内的存留量
- 服务器托管单线、双线以及多线如何区别
- 程序员常用的代码编辑器
- excel计算一年第几周
- 分享66个ASP上传下载源码,总有一款适合您
- 2023年法定节假日信息表-MySQL
- Linux引导过程和GRUB引导器
- 混淆矩阵、召回率、精确率、正确率、F1、真阳性率、假阳性率、ROC、AUC
- 服务器2012怎么换桌面背景,Windows Server 2012 R2桌面化详细设置图解
- 写给自己:入职两个月的收获与变化
- 蒙特卡洛树搜索 棋_蒙特卡罗树搜索赢得黑白棋