Java 8可选

在编程时,我们都面临着最( 臭名昭著 )的NullPointerException 。 而且我相信我们所有人都同意,遇到NullPointerException也是一种痛苦。 为了使读者了解最新情况,著名的计算机科学家Tony Hoare引入了引用,他认为这是一百万美元的错误 。 众所周知,这很容易实现,但是也很难预测。 这就是为什么开发人员需要非常谨慎的原因。

通常的方式

让我们考虑以下3个简单的POJO。

public class Employee {private Car car;public Car getCar() {return car;}
}public class Car {private Insurance insurance;public Insurance getInsurance() {return insurance;}
}public class Insurance {private String name;public String getName() {return name;}
}

仅提供背景信息-员工可以拥有汽车(虽然不是强制性的),汽车可以具有保险(不一定),并且保险必须始终具有名称。 只要记住了解以下内容即可。

现在,我们想通过提供人员实例来获得保险的名称。

public String getInsuranceName(Employee employee) {if (employee != null) {Car car = employee.getCar();if (car != null) {Insurance insurance = car.getInsurance();if (insurance != null) {return insurance.getName();}}}return "UNKNOWN";
}

这是我们通常采取的预防措施,这样就不会遇到可怕的NullPointerException 。 我们还认为这也会污染源代码,根据我的观点,应将其视为反模式。

另一种惯用的方式

上一节中提到的对null检查的这种深层嵌套看起来有些晦涩。 有时人们会以不同的方式来做。

public String getInsuranceName(Employee employee) {if (employee == null) {return "UNKNOWN";}Car car = employee.getCar();if (car == null) {return "UNKNOWN";}Insurance insurance = car.getInsurance();if (insurance == null) {return "UNKNOWN";}return insurance.getName();
}

在我看来,这还算不错,因为它不包含深层嵌套的null检查。 但是它仍然遵循相同的反模式,以不同的方式检查空值。

为什么NULL不好?

  1. 这会降低源代码的可读性
  2. 呈现没有价值的东西在语义上是不正确的
  3. 它与Java的思想背道而驰,因为Java会向开发人员隐藏指针(除非存在空引用的情况)

NULL的替代

很少有语言(例如Scala,Groovy)消除了对空引用(表示没有值)的可怕使用。 可以以非常简洁的方式用Groovy编写类似的代码。

def name = employee?.car?.insurance?.name

这在Groovy中被称为“ 安全导航”操作符 ,它清楚地显示了易读的代码,同时消除了遇到可怕的空引用的可能性。

Java的努力

现在我们应该问,Java开发人员可以做什么来实现类似的事情,从而在保持可读性和可维护性的源代码的同时,防止NullPointerException的可能性。 Java语言设计人员选择了Groovy或Scala语言已经实现的类似方法,但是引入了一个新类-Optional

可选的

public final class Optional<T> {public static<T> Optional<T> empty() {}public static <T> Optional<T> of(T value) {}public static <T> Optional<T> ofNullable(T value) {}public T get() {}public boolean isPresent() {}public void ifPresent(Consumer<? super T> consumer) {}public Optional<T> filter(Predicate<? super T> predicate) {}public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {}public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {}public T orElse(T other) {}public T orElseGet(Supplier<? extends T> other) {}public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {}
}

此类主要用于表示值的不存在。 如果您认为一个值可以始终存在或不能始终存在,则最好使用Optional类型。 在我们前面的示例中,员工可能会或可能不会载有汽车,这就是为什么最好返回Optional <Car>而不是简单地返回Car

让我们看看我们如何设计上一个示例:

public class Employee {private Car car;public Optional<Car> getCar() {return Optional.ofNullable(car);}
}public class Car {private Insurance insurance;public Optional<Insurance> getInsurance() {return Optional.ofNullable(insurance);}
}public class Insurance {private String name;public String getName() {return name;}
}

我没有讨论过静态工厂ofNullable(..)方法,而只是将其视为包装值的包装实用程序方法,而不管其引用如何。

只需查看API,就可以轻松了解遇到可选类型时需要执行的操作。 对于开发人员而言,遇到此类可选类型总是表示缺少值的可能性,因此开发人员可以为此采取适当的措施。

可选创作

从类概述中,我们可以清楚地看到可以以多种方式创建Optional

  1. of(..) :这允许创建包装非空值的Optional实例
  2. empty() :这将创建一个空的Optional
  3. ofNullable(..) :这允许创建一个包装任何值(空或非空)的Optional实例

可选的提取和转换

到目前为止,我们已经看到了如何创建Optional实例。 现在我们应该看看如何提取值或将其转换为另一个值。

  1. get()返回包含的值,如果Optional实例为空,则抛出NoSuchElementException

但是我们应该如何使用呢?

Car car = employee.getCar();
if (employee != null) {car = employee.getCar();
}

这是我们逃避NullPointerException的主要工作。 现在,使用Java 8 Optional ,我们可以编写如下代码:

Optional<Car> car = employee.getCar();
if (!car.isEmpty()) {Car car = car.get();
}

但是,您是否认为这是对讨厌的null检查的改进?

我曾经认为它是一种改进,因为它隐藏了空指针,但后来,我觉得它会污染源代码。 但是我不反对使用从方法或包装变量中返回Optional作为类型的方法。 我将在以下各节中讨论其背后的原因。

让我们考虑以前的方法:

public String getInsuranceName(Employee employee) {return employee.getCar().getInsurance().getName();
}

这是一个非常干净的代码,但是NullPointerException却藏在后面,这就是为什么我们需要合并几个空引用检查(我们之前已经看到过)的原因。

如果我们在设计一个好的API时合并了公共String Optional ,则可以通过更简洁的方式实现:

public String getInsuranceName(Optional<Employee> employee) {return employee.flatMap(Employee::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("UNKNOWN");
}

这是不是真的好又干净的方法? 我知道这会使一些对Java Streams API不满意的程序员感到困惑。 我强烈建议对Java 8 Streams有一个快速的了解,以了解Optional的优点。

另一个示例是如果人名以“ P”开头,则获得保险名称

public String getInsuranceName(Optional<Employee> employee) {return employee.filter(e-> e.getName().startsWith("P")).flatMap(Employee::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("UNKNOWN");
}

设计实践

现在,我想以一些不同的方式分享一些有关设计我们先前讨论的POJO的想法。

API设计实践1

public class Employee {private Optional<Car> car;public Optional<Car> getCar() {return car;}
}public class Car {private Optional<Insurance> insurance;public Insurance getInsurance() {return insurance;}
}public class Insurance {private String name;public String getName() {return name;}
}

在这里,我已声明成员变量为Optional类型。 根据我的观点,这也是非常用户友好的,并且此类的用户或消费者可以轻松理解此类的性质。 在这种情况下,员工汽车是Optional的 ,也就是说,员工可能可能没有汽车。

API设计实践2

public class Employee {private Car car;public Optional<Car> getCar() {return Optional.ofNullable(car);}
}public class Car {private Insurance insurance;public Optional<Insurance> getInsurance() {return Optional.ofNullable(insurance);}
}public class Insurance {private String name;public String getName() {return name;}
}

这也是非常直观的,但是缺乏清晰显示成员实例不存在的想法。 要了解任何系统,开发人员总是需要首先了解对象模型,而了解对象模型则需要我们了解领域对象。 在这种情况下,员工是拥有汽车的域对象,就像它对员工是强制性的一样。 但实际上,员工可能会或可能不会有汽车。 我们可以在获取或检索其值( getCar() )时实现它,然后当该方法返回Optional时 ,我们可能会注意到它缺少包含值的可能性。

使用什么?

它完全取决于开发人员。 我个人更喜欢第一种方法,因为很明显在理解领域模型方面很明显,而第二种方法在序列化方面具有优势。 由于Optional不实现Serializable ,因此在我们的第一种方法中它不可序列化。 如果我们使用DTO,则可以使我们的实现适应第二种方法。

方法或构造函数参数中的可选

正如我之前提到的,“ 可选”在班级中清楚地表明了消费者应该做的事情。 因此,如果构造函数或方法接受Optional元素作为参数,则意味着该参数不是必需的。

另一方面,我们需要付出使用Optional污染代码库的代价。 开发人员唯一要谨慎使用它。 我个人不希望在方法参数中使用Optional ,但如果需要,我们仍然可以将其包装在Optional实例中并对其执行必要的操作。

方法返回类型中的可选

Java语言架构师Brian Goetz还建议,如果有可能返回null,则在方法中返回Optional 。 我们已经在API设计规范2中看到了这一点。

从方法抛出异常或返回可选

多年来,Java开发人员遵循通常的方法引发异常来表示方法调用中的错误情况。

public static InputStream getInputStream(final String path) {checkNotNull(path, "Path cannot be null");final URL url = fileSystem.getEntry(path);InputStream xmlStream;try {xmlStream = url.openStream();return xmlStream;} catch (final IOException ex) {throw new RuntimeException(ex);}
}

如果此方法的使用者遇到RuntimeException ,那是由于打开与指定URL的连接时出现的问题。 另一方面,我们还可以通过以下方式使用Optional

public static Optional<InputStream> getInputStream(final String path) {checkNotNull(path, "Path cannot be null");final URL url = fileSystem.getEntry(path);InputStream xmlStream;try {xmlStream = url.openStream();return Optional.of(xmlStream);} catch (final IOException ex) {return Optional.empty();}
}

我认为这很直观,因为它清楚地表明它返回一个可能具有或没有值的Optional实例。 这就是为什么我倾向于从可能具有这种空遇到可能性的方法中返回Optional的原因。

私有方法中的可选返回类型

私有方法显然不是要理解或分析项目的任何重要部分。 因此,我认为我们仍然可以使用null检查来摆脱过多的Optional,但是如果您认为仍然可以以更简洁明了的方式使用该方法,则也可以返回Optional

为了更好地理解,我编写了一个示例,如下所示:

private void process(final String data) {try {final ItemList nList = doc.getChildNodes();for (int temp = 0; temp < nList.getLength(); temp++) {final Node nNode = nList.item(temp);final String key = nNode.getName();final String value = nNode.getValue();values.put(getAttribute(key).orElseThrow(IllegalArgumentException::new), value);}} catch (final Exception ex) {logger.error("{}", ex.getMessage(), ex);}
}private Optional<Attribute> getAttribute(final String key) {return Arrays.stream(Attribute.values()).filter(x -> x.value().filter(y -> y.equalsIgnoreCase(key)).isPresent()).findFirst();
}public static enum Attribute {A ("Sample1"),B ("Sample2"),C ("Sample3");private String value;private Attribute(String value) {this.value = value;}public Optional<String> value() {return Optional.ofNullable(value);}}

我本可以以更常用的方式编写第二种方法:

private Attribute getAttribute(final String key) {for (final Attribute attribute : Attribute.values()) {Optional<String> value = attribute.value();if (value.isPresent() && value.get().equalsIgnoreCase(key)) {return attribute;}}throw new IllegalArgumentException();
}

私有方法中返回返回Collection或其任何子类型的可选返回类型

作为第一个示例,请考虑您需要实现一种从Java中指定路径列出文件的方法的代码。

public static List<String> listFiles(String file) {List<String> files;try {files = Files.list(Paths.get(path));} catch (IOException e) {files = Arrays.asList("Could not list");}return files;
}

我们可以实现更简洁的代码,如下所示:

public static List<String> listFiles(String path) {return Files.list(Paths.get(path)).filter(Files::isRegularFile).collect(toList());
}

注意,简洁方法中的返回类型仍为List而不是Optional 。 最好遵循返回空列表的通常做法,而不是使用Optional

使用Optional的流方式更加简洁是非常有专利的。 可选的是实用程序数据容器,可帮助开发人员摆脱空引用。 另外,它确实提供了许多有用的方法来简化程序员的任务。 但是,如果开发人员不太了解Optional的主要用法,则Optional可能会被严重滥用,并可能污染代码库。 这就是为什么我强烈建议大家在Optional中使用面向流的方法,以帮助开发人员编写简洁且可维护的代码

翻译自: https://www.javacodegeeks.com/2017/07/java-8-optionals.html

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

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

相关文章

matlab cat函数_如何用Matlab编写贪吃蛇游戏?(持续更新)

今后我们实验室的研究重点将会聚焦在“基于游戏的测评”和”教育游戏化“这两个主题上&#xff0c;因此很有必要研究实现“爆款”游戏的一些基本的技术方法。这篇文章将介绍如何借助Matlab GUI 面向对象编程技术实现贪吃蛇游戏。所有的游戏都可以解构成至少两个层次&#xff1…

python程序设计案例课堂第二篇_Python程序设计案例课堂第二篇核心技术第十章图形用户界面...

第10章 图形用户界面保存并运行程序&#xff0c;结果如图10-1所示。C:\Users\Administrator>python d:\python\ch10\10.1.py图10-1 程序运行结果【案例剖析】上述代码的含义分析如下。(1) 第1行&#xff1a;加载tkinter 模块。(2) 第 2 行&#xff1a;使用tkinter 模块的Tk(…

fprintf函数的用法matlab_极力推荐这个Matlab教程

点击蓝字 关注我们儿童节快乐前些日子, 由于一些原因的需要, 又把曾经的毕业论文拿来, 改吧改吧, 发现有些地方真的惨目忍睹, 只怪当时太年轻, 没想到给自己挖了一个大坑, 不,, 应该称之为巨坑, 对于论文中涉及到的代码, 当时主要使用了伪代码的形式, 以及现学现用的Python, 可…

websockets_使用Java WebSockets,JSR 356和JSON映射到POJO的

websockets因此&#xff0c;我一直在研究Tyrus &#xff08;JSR 356 WebSocket for Java规范的参考实现&#xff09;。 因为我一直在寻找测试工具&#xff0c;所以我对在Java中同时运行客户端和服务器端感兴趣。 因此&#xff0c;恐怕此博客文章中没有HTML5。 在此示例中&…

查看mysql主从复制是否成功的命令_mysql主从复制 - hong查理的个人空间 - OSCHINA - 中文开源技术交流社区...

1.配置 my.cof服务器A(192.168.1.2)配置如下log-bin mysql-binserver-id 1expire-logs-days 7#日志设置最高7天replicate-do-db test #需要同步的binlog-ignore-db mysql #忽略同步的binlog-ignore-db information_schema #忽略同步的auto-increment-increment 2…

python抓取数据时失败_爬取数据缺失的补坑,Python数据爬取的坑坑洼洼如何铲平...

渣渣业余选手讲解&#xff0c;关于爬取数据缺失的补坑&#xff0c;一点点关于Python数据爬取的坑坑洼洼如何铲平&#xff0c;个人的一些心得体会&#xff0c;还有结合实例的数据缺失的补全&#xff0c;几点参考&#xff0c;仅供观赏&#xff0c;如有雷同&#xff0c;那肯定是我…

关于总决赛

可以将变量声明为final。 最终变量只能分配一次。 如果分配了最终变量&#xff0c;则将导致编译时错误&#xff0c;除非在分配前立即将其绝对取消分配。 分配任何最终变量后&#xff0c;将永远无法对其进行更改。 如果变量引用任何对象的任何实例&#xff0c;它将继续引用相同的…

java中void_关于java中void的用法?

阿波罗的战车void除了说明该方法没有返回值外&#xff0c;还有什么作用呢&#xff1f;构造方法同样也是没有返回值的&#xff0c;那它和void方法有什么区别呢&#xff1f;构造方法与方法又有什么区别&#xff1f;用具象的实物来表现的话三者有何种关系呢&#xff1f;id 除了说明…

卷积神经网络mnist手写数字识别代码_搭建经典LeNet5 CNN卷积神经网络对Mnist手写数字数据识别实例与注释讲解,准确率达到97%...

LeNet-5卷积神经网络是最经典的卷积网络之一&#xff0c;这篇文章就在LeNet-5的基础上加入了一些tensorflow的有趣函数&#xff0c;对LeNet-5做了改动&#xff0c;也是对一些tf函数的实例化笔记吧。环境 Pycharm2019Python3.7.6tensorflow 2.0 话不多说&#xff0c;先放完整源码…

glassfish_多种监视和管理GlassFish 3的方法

glassfishGlassFish 3支持多种监视和管理方法。 在本文中&#xff0c;我将简要介绍GlassFish提供的管理&#xff0c;监视和管理方法。 GlassFish管理控制台 GlassFish基于Web的管理控制台GUI可能是GlassFish管理最著名的界面。 默认情况下&#xff0c;运行GlassFish后&#xf…

java 阻塞锁_Java实现锁、公平锁、读写锁、信号量、阻塞队列、线程池等常用并发工具...

锁的实现锁的实现其实很简单&#xff0c;主要使用Java中synchronized关键字。public class Lock {private volatile boolean isLocked false;private Thread lockingThread null;public synchronized void lock() throws InterruptedExpection {while(isLocked){wait();}isLo…

flask-mail异步发送邮件_SpringBoot 2.0 集成 JavaMail ,实现异步发送邮件

一、JavaMail的核心API1、API功能图解2、API说明(1)、Message 类:javax.mail.Message 类是创建和解析邮件的一个抽象类子类javax.mail.internet.MimeMessage &#xff1a;表示一份电子邮件。 发送邮件时&#xff0c;首先创建出封装了邮件数据的 Message 对象&#xff0c; 然后把…

Java 9中什么是私有的?

在进行面试时&#xff0c;我发现大多数应聘者都不知道Java中的private修饰符真正意味着什么。 他们知道一些足以进行日常编码的事情&#xff0c;但还远远不够。 这不成问题。 足够了解就足够了。 但是&#xff0c;了解Java的一些内部工作仍然很有趣。 在极少数情况下&#xff0…

python国际象棋ai程序_用Python编写一个国际象棋AI程序

最近我用Python做了一个国际象棋程序并把代码发布在Github上了。这个代码不到1000行&#xff0c;大概20%用来实现AI。在这篇文章中我会介绍这个AI如何工作&#xff0c;每一个部分做什么&#xff0c;它为什么能那样工作起来。你可以直接通读本文&#xff0c;或者去下载代码&…

java switch case怎么判断范围_【转】Java期末复习攻略!

期末19年就这样要过去了&#xff0c;终于到了小时候作文里的未来呢&#xff01;然而&#xff0c;期末考试也随之来临了。不知大家“预习”的怎么样呢&#xff1f; 期末复习资料的放送快接近尾声了下面康康学长学姐们怎么教你们打java这个boss(下面是java大佬给大家的复习建议以…

java list 去重复元素_java List去掉重复元素的几种方式

使用LinkedHashSet删除arraylist中的重复数据(有序)List words Arrays.asList("a","b","b","c","c","d");HashSet setnew LinkedHashSet<>(words);for(String word:set){System.out.println(word);}使用Has…

spring aop示例_Spring JpaRepository示例(内存中)

spring aop示例这篇文章描述了一个使用内存中HSQL数据库的简单Spring JpaRepository示例。 该代码示例可从GitHub的Spring-JpaRepository目录中获得。 它基于带有注释的Spring-MVC-示例和此处提供的信息 。 JPA资料库 在此示例中&#xff0c;我们实现了一个虚拟bean&#xff1…

python人工智能入门优达视频_机器学习:优达教你搭建Python 环境的正确姿势

原标题&#xff1a;机器学习:优达教你搭建Python 环境的正确姿势为机器学习搭建好 Python 环境听起来简单&#xff0c;但有时候坑还不少。如果此前没有配置过类似的环境&#xff0c;很可能会苦苦折腾各种命令好几个小时。可是我明明只是想马上搞起来我的机器学习&#xff01; 在…

java web登录状态保持_java web用于保持状态的4种方法

方法一&#xff1a;网址重写通过在url地址后面添加若干的token作为查询字符串来实现。token的值一般为 键值url?key1value1&key2value2&...&keynvaluenurl与token之间需要用?分开&#xff0c;两个token之间则是需要用一个&符号隔开。此方法适用于token不需要…

python天天向上续2_2019/2/12 Python今日收获

Python day12——025&#xff0c;026字典&#xff1a;当索引不好用时 1.字典&#xff1a;python唯一的一个映射类型。用键值对存储数据&#xff0c;他的标志是大括号。一个键值组合叫一个项。键的类型既可以是字符串类型也可以是整形也可以是浮点型。 如&#xff1a;dict{1:one…