【java8新特性】——Optional详解(三)

一、简介

Optional类是Java8为了解决null值判断问题,借鉴google guava类库的Optional类而引入的一个同名Optional类,使用Optional类可以避免显式的null值判断(null的防御性检查),避免null导致的NPE(NullPointerException)。

我们来看一段代码:

    public static String getGender(Student student){if(null == student){return "Unkown";}return student.getGender();}

这是一个获取学生性别的方法,方法入参为一个Student对象,为了防止student对象为null, 做了防御性检查:如果值为null,返回"Unkown"。

再看使用Optional优化后的方法:

    public static String getGender(Student student){return Optional.ofNullable(student).map(u -> u.getGender()).orElse("Unkown");}

可以看到,Optional类结合lambda表达式的使用能够让开发出的代码更简洁和优雅。

二、Optional对象的创建

我们看下Optional类的部分源码:

    private static final Optional<?> EMPTY = new Optional<>();private final T value;private Optional() {this.value = null;}public static<T> Optional<T> empty() {@SuppressWarnings("unchecked")Optional<T> t = (Optional<T>) EMPTY;return t;}private Optional(T value) {this.value = Objects.requireNonNull(value);}public static <T> Optional<T> of(T value) {return new Optional<>(value);}public static <T> Optional<T> ofNullable(T value) {return value == null ? empty() : of(value);}

可以看出,Optional类的两个构造方法都是private型的,因此类外部不能显示的使用new Optional()的方式来创建Optional对象,但是Optional类提供了三个静态方法empty()、of(T value)、ofNullable(T value)来创建Optinal对象,示例如下:

// 1、创建一个包装对象值为空的Optional对象
Optional<String> optStr = Optional.empty();
// 2、创建包装对象值非空的Optional对象
Optional<String> optStr1 = Optional.of("optional");
// 3、创建包装对象值允许为空的Optional对象
Optional<String> optStr2 = Optional.ofNullable(null);

三、Optional 类典型接口的使用

下面以一些典型场景为例,列出Optional API常用接口的用法,并附上相应代码。

3.1 get()方法

简单看下get()方法的源码:

    public T get() {if (value == null) {throw new NoSuchElementException("No value present");}return value;}

可以看到,get()方法主要用于返回包装对象的实际值,但是如果包装对象值为null,会抛出NoSuchElementException异常。

3.2 isPresent()方法

isPresent()方法的源码:

    public boolean isPresent() {return value != null;}

可以看到,isPresent()方法用于判断包装对象的值是否非空。下面我们来看一段糟糕的代码:

    public static String getGender(Student student){Optional<Student> stuOpt =  Optional.ofNullable(student);if(stuOpt.isPresent()){return stuOpt.get().getGender();}return "Unkown";}

这段代码实现的是第一章(简介)中的逻辑,但是这种用法不但没有减少null的防御性检查,而且增加了Optional包装的过程,违背了Optional设计的初衷,因此开发中要避免这种糟糕的使用~

3.3 ifPresent()方法

ifPresent()方法的源码:

    public void ifPresent(Consumer<? super T> consumer) {if (value != null)consumer.accept(value);}

ifPresent()方法接受一个Consumer对象(消费函数),如果包装对象的值非空,运行Consumer对象的accept()方法。示例如下:

    public static void printName(Student student){Optional.ofNullable(student).ifPresent(u ->  System.out.println("The student name is : " + u.getName()));}

上述示例用于打印学生姓名,由于ifPresent()方法内部做了null值检查,调用前无需担心NPE问题。

3.4 filter()方法

filter()方法的源码:

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

filter()方法接受参数为Predicate对象,用于对Optional对象进行过滤,如果符合Predicate的条件,返回Optional对象本身,否则返回一个空的Optional对象。举例如下:

    public static void filterAge(Student student){Optional.ofNullable(student).filter( u -> u.getAge() > 18).ifPresent(u ->  System.out.println("The student age is more than 18."));}

上述示例中,实现了年龄大于18的学生的筛选。

3.5 map()方法

map()方法的源码:

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

map()方法的参数为Function(函数式接口)对象,map()方法将Optional中的包装对象用Function函数进行运算,并包装成新的Optional对象(包装对象的类型可能改变)。举例如下:

    public static Optional<Integer> getAge(Student student){return Optional.ofNullable(student).map(u -> u.getAge()); }

上述代码中,先用ofNullable()方法构造一个Optional对象,然后用map()计算学生的年龄,返回Optional对象(如果student为null, 返回map()方法返回一个空的Optinal对象)。

3.6 flatMap()方法

flatMap()方法的源码:

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

跟map()方法不同的是,入参Function函数的返回值类型为Optional</U/>类型,而不是U类型,这样flatMap()能将一个二维的Optional对象映射成一个一维的对象。以3.5中示例功能为例,进行faltMap()改写如下:

    public static Optional<Integer> getAge(Student student){return Optional.ofNullable(student).flatMap(u -> Optional.ofNullable(u.getAge())); }

3.7 orElse()方法

orElse()方法的源码:

    public T orElse(T other) {return value != null ? value : other;}

orElse()方法功能比较简单,即如果包装对象值非空,返回包装对象值,否则返回入参other的值(默认值)。如第一章(简介)中提到的代码:

    public static String getGender(Student student){return Optional.ofNullable(student).map(u -> u.getGender()).orElse("Unkown");}

3.8 orElseGet()方法

orElseGet()方法的源码:

    public T orElseGet(Supplier<? extends T> other) {return value != null ? value : other.get();}

orElseGet()方法与orElse()方法类似,区别在于orElseGet()方法的入参为一个Supplier对象,用Supplier对象的get()方法的返回值作为默认值。如:

    public static String getGender(Student student){return Optional.ofNullable(student).map(u -> u.getGender()).orElseGet(() -> "Unkown");      }

3.9 orElseThrow()方法

orElseThrow()方法的源码:

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

orElseThrow()方法其实与orElseGet()方法非常相似了,入参都是Supplier对象,只不过orElseThrow()的Supplier对象必须返回一个Throwable异常,并在orElseThrow()中将异常抛出:

    public static String getGender1(Student student){return Optional.ofNullable(student).map(u -> u.getGender()).orElseThrow(() -> new RuntimeException("Unkown"));      }

orElseThrow()方法适用于包装对象值为空时需要抛出特定异常的场景。

四、注意事项

使用Optional开发时要注意正确使用Optional的“姿势”,特别注意不要使用3.2节提到的错误示范,谨慎使用isPresent()和get()方法,尽量多使用map()、filter()、orElse()等方法来发挥Optional的作用。


  • 【java8新特性】——lambda表达式与函数式接口详解(一)

  • 【java8新特性】——Stream API详解(二)

  • 【java8新特性】——Optional详解(三)

  • 【java8新特性】——方法引用(四)

  • 【java8新特性】——默认方法(五)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/557291.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不使用 + 和 - 运算符计算两整数之和

问题概述 不使用运算符 和 -&#xff0c;计算两整数之和 思考 不使用 和 - &#xff0c;那就只能想到用位运算来处理了。思路如下&#xff1a; 两数进行 ^&#xff08;异或运算&#xff09;&#xff0c;可以得到两个数在相同位上数值不同的相加结果两数进行 &&#x…

vts传感器采取船舶的_详解虎门大桥监测系统:传感器与物联网功不可没

来源&#xff1a;传感器专家网近日&#xff0c;虎门大桥“虎躯一震”给全国人民来了个“深呼吸”。虎门大桥是广东沿海地区重要的交通枢纽&#xff0c;始建于1992年&#xff0c;1997年通车至今&#xff0c;大桥一直都十分平稳。但在5月5日下午&#xff0c;虎门大桥发生异常抖动…

宝塔安装sqlserver_linux宝塔面板安装安装 pdo_sqlsrv扩展

第一步安装源curl https://packages.microsoft.com/config/rhel/7/prod.repo > /etc/yum.repos.d/mssqlrelease.repo第二步安装驱动yum install msodbcsql mssql-tools unixODBC-devel第三步下载pdo-sqlsrv源码wget http://pecl.php.net/get/pdo_sqlsrv-5.6.1.tgztar -zxvf …

【java8新特性】——方法引用(四)

一、简介 方法引用是java8的新特性之一&#xff0c; 可以直接引用已有Java类或对象的方法或构造器。方法引用与lambda表达式结合使用&#xff0c;可以进一步简化代码。 来看一段简单代码&#xff1a; public static void main(String[] args) {List<String> strList Ar…

MySQL 排名函数.md

概述 MySQL 自带的排名的函数&#xff0c;主要有&#xff1a; row_number()rank()dense_rank()ntile() 测试数据 测试数据如下所示&#xff1a; row_number() 函数 用法如下&#xff1a; SELECT row_number() OVER (ORDER BY Salary DESC) row_num,Salary FROMEmployee查…

【java8新特性】——默认方法(五)

一、简介 默认方法是指接口的默认方法&#xff0c;它是java8的新特性之一。顾名思义&#xff0c;默认方法就是接口提供一个默认实现&#xff0c;且不强制实现类去覆写的方法。默认方法用default关键字来修饰。 默认方法可以解决的痛点&#xff1a; 在java8之前&#xff0c;修…

Java 序列化总结.md

概述 序列化&#xff1a;将对象写入到 IO 流中反序列化&#xff1a;从 IO 流中恢复对象 实现方法 实现 Serializable 或者 Externalizable Serializable&#xff1a;标记接口&#xff0c;不用实现任何方法&#xff0c;可以指定序列化 IDExternalizable&#xff1a;增强的序…

多线程买票案例

测试类 package thead;public class testThread {public static void main(String [] arg){Tickets ticket new Tickets();Thread t1 new Thread(ticket,"窗口一&#xff1a;");Thread t2 new Thread(ticket,"窗口二&#xff1a;");Thread t3 new Thr…

深度学习auc_机器学习集成学习与模型融合!

↑↑↑关注后"星标"Datawhale每日干货 & 每月组队学习&#xff0c;不错过Datawhale干货 作者&#xff1a;李祖贤&#xff0c;深圳大学&#xff0c;Datawhale高校群成员对比过kaggle比赛上面的top10的模型&#xff0c;除了深度学习以外的模型基本上都是集成学习的…

常用并发工具类(锁和线程间通信工具类)

常用并发工具类总结 JUC 下的常用并发工具类&#xff08;锁和线程间通信工具类&#xff09;&#xff0c;主要包括 ReentrantLock、ReentrantReadWriteLock、CountDownLatch、CyclicBarrier、Semaphore、Exchanger ReentrantLock 和 ReentrantReadWriteLock ReentrantLock 是…

of方法:给集合一次性添加多个元素

of()方法只是Map&#xff0c;List&#xff0c;Set这三个接口的静态方法&#xff0c;其父类接口和子类实现并没有这类方法&#xff0c;比如 HashSet&#xff0c;ArrayList返回的集合是不可变的&#xff0c;再次添加会报错Set与Map集合不可以存储重复的元素&#xff0c;否则会报错…

数控车椭圆编程实例带图_数控车床编程教程,图文实例详解

一、数控车编程特点(1) 可以采用绝对值编程(用X、Z表示)、增量值编程(用U、W表示)或者二者混合编程。(2) 直径方向(X方向) 系统默认为直径编程&#xff0c;也可以采用半径编程&#xff0c;但必须更改系统设定。(3) X向的脉冲当量应取Z向的一半。(4)采用固定循环&#xff0c;简化…

常用并发工具类(并发集合类)

文章目录概述BlockingQueueArrayBlockingQueue数据存储相关属性阻塞特性相关属性主要方法LinkedBlockingQueueLinkedBlockingQueue 主要属性LinkedBlockingQueue 设计思想ConcurrentLinkedQueuePriorityBlockingQueuePriorityBlockingQueue 主要属性PriorityBlockingQueue 设计…

参考文献起止页码怎么写_毕业论文文献综述不会写?快来看看这篇文章(附含通用模板)...

文献综述是对所研究主题的现状进行客观的叙述和评论、寻求新的研究突破点。一个资料全面、研究深入的综述不仅可以帮助作者确立毕业论文的选题&#xff0c;还可以为论文的深入研究提供有力的支撑。本文分享一份"毕业论文文献综述万能模板",以供参考。一、文献综述的基…

常用并发工具类(线程池)

文章目录概述ThreadPoolExecutorThreadPoolExecutor 的主要属性Worker 主要属性线程池的状态线程池的状态流转线程池提交任务的执行流程线程数量的设置线程池的种类FixedThreadPoolCachedThreadPoolSingleThreadExecutorScheduledThreadPoolExecutorSingleThreadScheduledExecu…

【Java 8 新特性】Java Stream.of()用法示例

本页将介绍Java Stream.of方法示例。Stream.of用于为给定元素创建顺序流。我们可以传递单个元素或多个元素。 查看javadoc中Stream.of方法声明。 static <T> Stream<T> of(T t) 参数&#xff1a;传递单个元素。 返回&#xff1a;该方法返回一个包含一个元素的流。…

Java 类加载机制

文章目录概述类的生命周期类加载的时机类加载的主要 5 个阶段加载验证准备准备阶段初始值的含义解析符号引用直接引用解析阶段的理解静态绑定与动态绑定初始化类加载器类加载器与类之间的关系类加载器的种类双亲委派机制双亲委派机制设计目的破坏双亲委派机制破坏双亲委派机制的…

Java –什么是瞬态字段?

在Java中&#xff0c; transient字段在序列化过程中被排除。 简而言之&#xff0c;当我们将对象保存到文件中&#xff08;序列化&#xff09;时&#xff0c;所有transient字段都将被忽略。 1. POJO 瞬态 复查以下Person类&#xff1b; 薪水领域是transient 。 public class …

JVM 内存模型与内存分配方式

文章目录JVM 内存模型概述基于分代收集理论设计的垃圾收集器所管理的堆结构方法区的演变内存分配划分内存的方法划分内存时如何解决并发问题对象栈上分配基于分代收集理论的垃圾收集器管理下的内存分配规则对象优先分配在 Eden 区大对象直接进入老年代长期存活的对象将逐步进入…

image pil 图像保存_如何利用python中的PIL库做图像处理?

自从这个世界上出现了Python编程&#xff0c;一切都好像有了新的思路与进展&#xff0c;比如人工智能&#xff0c;还有我们常用的PS&#xff0c;你可知道Python也可以做图像处理&#xff0c;用的就是PIL库&#xff0c;还没有用过的&#xff0c;还没有发现的&#xff0c;还没有实…