首页 » 网站建设 » phpgojson技巧_京东云开拓者|深入JDK中的Optional

phpgojson技巧_京东云开拓者|深入JDK中的Optional

访客 2024-11-27 0

扫一扫用手机浏览

文章目录 [+]

1、办理的问题

臭名昭著的空指针非常,是每个程序员都会碰着的一种常见非常,任何访问工具的方法与属性的调用,都可能会涌现NullPointException,如果要确保不触发非常,我们常日须要进行工具的判空操作。

phpgojson技巧_京东云开拓者|深入JDK中的Optional

举个栗子,有一个人(Shopper)进超市购物,可能会用购物车(Trolley)也可能会用其它办法,购物车里可能会有一袋栗子(Chestnut),也可能没有。
三者定义的代码如下:

phpgojson技巧_京东云开拓者|深入JDK中的Optional
(图片来自网络侵删)

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic class Shopper { private Trolley trolley; public Trolley getTrolley(){ return trolley; }}public class Trolley { private Chestnut chestnut; public Chestnut getChestnut(){ return chestnut; }}public class Chestnut { private String name; public String getName(){ return name; }}

这时想要得到购物车中栗子的名称,像下面这么写,就可能会见到我们的“老朋友”(NPE)

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic String result(Shopper shopper){ return shopper.getTrolley().getChestnut().getName();}

为了能避免涌现空指针非常,常日的写法会逐层判空(多层嵌套法),如下

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic String result(Shopper shopper) { if (shopper != null) { Trolley trolley = shopper.getTrolley(); if (trolley != null) { Chestnut chestnut = trolley.getChestnut(); if (chestnut != null) { return chestnut.getName(); } } } return "获取失落败辽"; }

多层嵌套的方法在工具级联关系比较深的时候会看得眼花缭乱的,尤其是那一层一层的括号;其余出错的缘故原由也由于缺少对应信息而被模糊(例如trolley为空时也只返回了末了的获取失落败。
当然也可以在每一层增加return,相应的代码有会变得很冗长),以是此时我们也可以用遇空则返回的卫语句进行改写。

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic String result(Shopper shopper) { if (shopper == null) { return "购物者不存在"; } Trolley trolley = shopper.getTrolley(); if (trolley == null) { return "购物车不存在"; } Chestnut chestnut = trolley.getChestnut(); if (chestnut == null) { return "栗子不存在"; } return chestnut.getName();}

为了取一个名字进行了三次显示判空操作,这样的代码当然没有问题,但是精良的工程师们总是希望能得到更优雅简洁的代码。
Optional就供应了一些方法,实现了这样的期望。

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic String result(Shopper shopper){ return Optional.ofNullable(shopper) .map(Shopper::getTrolley) .map(Trolley::getChestnut) .map(Chestnut::getName) .orElse("获取失落败辽");}

2、常用方法

1)得到Optional工具

Optional类中有两个布局方法:带参和不带参的。
带参的将传入的参数赋值value,从而构建Optional工具;不带参的用null初始化value构建工具。

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonprivate Optional() {}private Optional(T value) {}

但是两者都是私有方法,而实际上Optional的工具都是通过静态工厂模式的办法构建,紧张有以下三个函数

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic static <T> Optional<T> of(T value) {}public static <T> Optional<T> ofNullable(T value) {}public static <T> Optional<T> empty() {}

创建一个一定不为空的Optional工具,由于如果传入的参数为空会抛出NPE

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonChestnut chestnut = new Chestnut();Optional<Chestnut> opChest = Optional.of(chestnut);

创建一个空的Optional工具

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonOptional<Chestnut> opChest = Optional.empty();

创建一个可空的Optional工具

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonChestnut chestnut = null;Optional<Chestnut> opChest = Optional.ofNullable(chestnut);

2)正常利用

正常利用的方法可以被大致分为三种类型,判断类、操作类和取值类

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython//判断类public boolean isPresent() {}//操作类public void ifPresent(Consumer<? super T> consumer) {}//取值类public T get() {}public T orElse(T other) {}public T orElseGet(Supplier<? extends T> other) {}public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {}

isPresent()方法像一个安全阀,掌握着容器中的value值是空还是有值,用法与原来的null != obj的用法相似。
当obj有值返回true,为空返回false(即value值存在为真)。
但一样平常实现判断空或不为空的逻辑,利用Optional其他的方法处理会更为常见。
如下代码将会打印出没有栗子的悲惨事实。

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonOptional<Chestnut> opChest = Optional.empty();if (!opChest.isPresent()){ System.out.println("容器里没有栗子");}

ifPresent()方法是一个操作类的方法,他的参数是一段目标类型为Consumer的函数,当value不为空时,自动实行consumer中的accept()方法(传入时实现),为空则不实行任何操作。
比如下面这段代码,我们传入了一段输出value的lamda表达式,打印出了“迁西板栗”。

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonOptional<Chestnut> opChest = Optional.ofNullable(new Chestnut("迁西板栗"));opChest.ifPresent(c -> System.out.println(c.getName()));

get()方法源码如下,可以看出,get的浸染是直接返回容器中的value。
但如此粗暴的方法,利用前如果不判空,在value为空时,便会绝不留情地抛出一个非常。

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value;}

三个orElse方法与get相似,也都属于取值的操作。
与get不同之处在于orElse方法不用额外的判空语句,撰写逻辑时比较愉快。
三个orElse的相同之处是当value不为空时都会返回value。
当为空时,则另有各自的操作:orElse()方法会返回传入的other实例(也可以为Supplier类型的函数);orElseGet()方法会自动实行Supplier类型实例的get()方法;orElseThrow()方法会抛出一个自定的非常。
更详细的差别会在后面的方法比拟中描述。

如下面这段代码,展示了在没有栗子的时候,如何吐出“信阳板栗”、“镇安板栗”,以及抛出“抹油栗子”的警告。

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonOptional<Chestnut> opChest = Optional.empty();System.out.println(opChest.orElse(new Chestnut("信阳板栗")));System.out.println(opChest.orElseGet(() -> new Chestnut("镇安板栗")));try { opChest.orElseThrow(() -> new RuntimeException("抹油栗子呀"));}catch (RuntimeException e){ System.out.println(e.getMessage());}

3)进阶利用

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic 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) {}

filter()方法接管谓词为Predicate类型的函数作为参数,如果value值不为空则自动实行predicate的test()方法(传入时实现),来判断是否知足条件,知足则会返回自身Optional,不知足会返回空Optional;如果value值为空,则会返回自身Optional(实在跟空Optional也差不多)。
如下代码,第二句中筛选条件“邵店板栗”与opChest中的板栗名不符,没有通过过滤。
而第三句的筛选条件与opChest同等,以是末了打印出来的是“宽城板栗”。

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonOptional<Chestnut> opChest = Optional.ofNullable(new Chestnut("宽城板栗"));opChest.filter(c -> "邵店板栗".equals(c.getName())).ifPresent(System.out::println);opChest.filter(c -> "宽城板栗".equals(c.getName())).ifPresent(System.out::println);

map()和flatmap()方法传入的都是一个Function类型的函数,map在这里翻译为“映射”,当value值不为空时进行一些处理,返回的值是经由mapper的apply()方法处理后的Optional类型的值,两个方法的结果同等,处理过程中会有差别。
如下代码,从opChest中获取了板栗名后,重新new了一个板栗取名“邢台板栗”,并打印出来,两者输出同等,处理形式上有差异,这个在后面的方法比拟中会再次说到。

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonOptional<Chestnut> opChest = Optional.ofNullable(new Chestnut("邢台板栗"));System.out.println(opChest.map(c -> new Chestnut(c.getName())));System.out.println(opChest.flatMap(c -> Optional.ofNullable(new Chestnut(c.getName()))));

4)1.9新增

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {}public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) {}public Stream<T> stream() {}

JDK1.9中增加了三个方法:ifPresentOrElse() 、or()和stream()方法。

1.8时,ifPresent()仅供应了if(obj != null)的方法,并未供应if(obj != null)else{}的操作,以是在1.9中增加了一个ifPresentElse()方法,供应了这方面的支持。
该方法吸收两个参数Consumer和Runnable类型的函数,当value不为空,调用action的accept()方法,这点与ifPresent()同等,当value为空时,会调用emptyAction的run()方法,实行else语义的逻辑。
如下面代码,会打印出“木有栗子”的提示。

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonOptional<Chestnut> opChest = Optional.empty();opChest.ifPresentElse(c -> System.out.println(c.getName()),c -> System.out.println("木有栗子呀"));

or()方法是作为orElse()和orElseGet()方法的改进而涌现的,利用方法同等,但后两个方法在实行完成后返回的并非包装值。
如果须要实行一些逻辑并返回Optional时,可以利用or()方法。
该方法传入Supplier接口的实例,当value有值时直接返回自身Optional,当为空时,自动实行suuplier的get()方法,并包装成Optional返回,其源码中包装的语句如下:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonOptional<T> r = (Optional<T>) supplier.get();return Objects.requireNonNull(r);

stream()方法则不用多说,是一个供应给流式编程利用的方法,功能上是一个适配器,将Optional转换成Stream:没有值返回一个空的stream,或者包含一个Optional的stream。
其源码如下:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonif (!isPresent()) { return Stream.empty();} else { return Stream.of(value);}

3、方法比拟和总结

Optional封装的方法较多,选择一个得当的方法的条件是要理解各自适用的场景和异同

1)创建方法的比拟

由于布局器为私有方法,创建工具只能通过静态工厂的办法创建。
of()、ofNullable()和empty()方法是三个静态方法。
先上源码:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython//工厂方法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);}public static<T> Optional<T> empty() { @SuppressWarnings("unchecked") Optional<T> t = (Optional<T>) EMPTY; return t;}//布局方法private Optional() { this.value = null;}private Optional(T value) { this.value = Objects.requireNonNull(value);}//静态常量private static final Optional<?> EMPTY = new Optional<>()

of()方法通过调用带参布局,new出一个Optional工具,正常形参带值是不会有问题的,但是当形参为空时,设置value前的Objects.requireNonNull()非空校验,就会抛出一个非常,代码如下:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic static <T> T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj;}

requireNonNull()方法是java.util包下Objects类的一个方法,浸染是检讨传入的参数是否为空,为空会抛出一个NPE,在Optional类中用到的地方还有很多。
以是只有确信布局Optional所传入的参数不为空时才可利用of()方法。

与of()相对的还有一个ofNullable()方法,该方法许可接管null值布局Optional,当形参为null时,调用empty()方法,而empty()方法返回的是一个编译期就确定的常量EMPTY,EMPTY取值是无参布局器创建工具,终极得到的是一个value为空的Optional工具。

2)利用方法的比拟

2.2)中说到,正常利用的方法中有属于取值类的方法,orElse()、orElseGet()和orElseThrow(),这三个方法在非空时均返回value,但是为空时的处理各不相同。
先上源码:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic T orElse(T other) { return value != null ? value : other;}public T orElseGet(Supplier<? extends T> other) { return value != null ? value : other.get();}public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); }

orElse()和orElseGet()方法最直不雅观的差异是形参的不同,看下面一段代码:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython//测试语句Optional<Chestnut> opChest = Optional.ofNullable(new Chestnut("桐柏板栗"));//Optional<Chestnut> opChest = Optional.empty();opChest.orElse(print("orELse"));opChest.orElseGet(()->print("orElseGet"));//调用方法private static Chestnut print(String method){ System.out.println("燕山板栗最好吃----"+method); return new Chestnut("燕山板栗");}

第一次,new出一个“桐柏板栗”的Optional,分别调用orElse()和orElseGet()方法,结果涌现了两行的“燕山板栗最好吃”的输出,由于两个方法在value不为null时都会实行形参中的方法;

第二次,通过empty()方法得到一个空栗子的Optional,再调用orElse()和orElseGet()方法,结果居然还涌现了一行“燕山板栗最好吃”的输出。

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython第一次输出:燕山板栗最好吃----orELse燕山板栗最好吃----orElseGet第二次输出:燕山板栗最好吃----orELse

其缘故原由是orElseGet()的参数是Supplier目标类型的函数,大略来说,Suppiler接口类似Spring的

这个特性在一些大略的方法上差距不大,但是当方法是一些实行密集型的调用时,比如远程调用,打算类或者查询类方法时,会损耗一定的性能。

orElseThrow()方法与orElseGet()方法的参数都是函数类型的,这意味着这两种方法都是

3)其他方法的比拟

a、map与filterMap

先上源码:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic<U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Optional.ofNullable(mapper.apply(value)); }}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()与filterMap()的相同点是,都接管一个Function类型的函数,并且返回值都是Optional类型的数据。
但是从源码中我们也能看出:

首先,map()在返回时,利用了ofNullable()函数对返回值包了一层,这个函数在2.1)已经说过,是一个Optional的工厂函数,浸染是将一个数据包装成Optional;而filterMap()返回时只是做了非空校验,在运用mapper.apply时就已经是一个Optional类型的工具。

其次,从署名中也可以看出,map()的Function的输出值是"? extends U",这意味着在mapper.apply()处理完成后,只要吐出一个U类型或者U类型的子类即可;而filterMap()的Functional的输出值是“Optional<U>”,则在mapper.apply()处理完成之后,返回的必须是一个Optional类型的数据。

b、ifPresent与ifPresentOrElse

ifPresentOrElse()方法是作为ifPresent()的改进方法涌现的。
先看源码:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic void ifPresent(Consumer<? super T> action) { if (value != null) { action.accept(value); }}public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) { if (value != null) { action.accept(value); } else { emptyAction.run(); }}

从源码中可以看出,ifPresentOrElse()参数增加了一个Runnable类型的函数emptyAction,在value != null时,都激活了action.accept()方法。
只是当value == null时,ifPresentOrElse()方法还会调用emptyAction.run()方法。
以是总的来说,jdk1.9加入ifPresentOrElse()方法,是作为ifPreset在if-else领域的补充涌现的。

c、or与orElse

同样作为改进的or()方法也是为理解决orElse系列方法的“小缺陷”涌现的,先看源码:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) { Objects.requireNonNull(supplier); if (isPresent()) { return this; } else { @SuppressWarnings("unchecked") Optional<T> r = (Optional<T>) supplier.get(); return Objects.requireNonNull(r); }}public T orElse(T other) { return value != null ? value : other;}public T orElseGet(Supplier<? extends T> supplier) { return value != null ? value : supplier.get();}

or()方法在署名形式上更靠近orElseGet(),即形参都是Supplier类型的函数,但是与其不同的是,or()方法在形参中,指定了Supplier返回的类型必须为Optional类型,且value的类型必须为T或者T的子类。
orElse系列的方法,更像是一莳花费的方法,从一个Optional的实例中“取出“value的值进入下一步操作,而or()方法则像是建造者模式,对value有一定的操作之后,重新吐出的还是Optional类型的数据,以是利用时可以串联在一起,后一个or处理前一个or吐出的Optional。

4)“危险”方法的比拟

这里指的“危险”指的是会抛出非常,毕竟引进Optional类的目的便是去除对NPE的判断,如果此时再抛出一个NPE或者其他的非常,没有处理好就会为程序引入不小的麻烦。
以是对Optional中可能抛出非常的方法做一个总结。

首先,最直不雅观的会抛出非常的方法便是of()方法,由于of方法会调用带参布局创建实例,而带参布局中有对value非空的检讨,如果空会抛出NPE非常;

其次,get()方法也是一个“危险”的方法,由于当不判空直策应用get取值时,会触发get中NoSuchElementException非常;

再次,orElseThrow()方法也会抛出非常,但是这种非常属于人为指定的非常,是为了使得非常情形的语义更加丰富,而人为设置的,是一种可控的非常;

末了,在一些方法中,设置了参数非空检讨(Objects.requireNonNull()),这种检讨会抛出NPE非常,撤除已经提到的带参布局器,还有filter、map、flatMap、or这四个方法,如果传入的接口实例是Null值就会随时引爆NPE。

4、误用形式与Best practice

1)误用形式

a、初始化为null

第一种误用形式是给Optional类型的变量初始化的时候.Optional类型变量是默认不为空的,以是在取方法实行的时候才可以肆无忌惮"点"出去,如果在初始化的时候涌现:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonOptional<Chestnut> chest = null;

并且不及时为chest赋值,则还是随意马虎涌现NPE,精确的初始化办法该当是:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonOptional<Chestnut> chest = Optional.empty();

b、大略判空

第二种比较常见的误用形式该当是利用isPresent()做大略判空。
原来的代码如下:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic String getName(Chestnut chestnut){if(chestnut == null){return "栗子不存在";}else return chestnut.name();}

代码中,通过检讨chestnut == null来处理为空时的情形,大略利用isPresent()方法判空的代码如下:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic String getName(Chestnut chestnut){Optional<Chestnut> opChest = Optional.ofNullable(chestnut);if(!opChest.isPresent()){return "栗子不存在";}else return opChest.getname();}

酱婶儿并没有太大差别,以是在利用Optional时,首先应避免利用 Optional.isPresent() 来检讨实例是否存在,由于这种办法和 null!= obj 没有差异也没什么意义。

c、大略get

第三种比较常见的误用形式是利用Optional.get()办法来获取Optional中value的值,get()方法中对value==null的情形有抛出非常,以是该当在做完非空校验之后再从get取值,或者十分确定value一定不为空,否则会涌现NoSuchElementException的非常。
相对的,如果不是很确信,则利用orElse(),orElseGet(),orElseThrow()得到你的结果会更加得当。

d、作为属性字段和方法参数

第四种误用形式在初学Optional的时候随意马虎碰到,当指定某个类中的属性,或者方法的参数为Optional的时候,idea会给出如下提示:

Reports any uses of java.util.Optional<T>, java.util.OptionalDouble, java.util.OptionalInt, java.util.OptionalLong or com.google.common.base.Optional as the type for a field or parameter. Optional was designed to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result". Using a field with type java.util.Optional is also problematic if the class needs to be Serializable, which java.util.Optional is not.

大意是不建议如此利用Optional。
第一,不建议利用Optional作为字段或参数,其设计是为库方法返回类型供应一种有限的机制,而这种机制可以清晰的表示“没有结果”的语义;第二,Optional没有实现Serilazable,是不可被序列化的。

这种误用方法比较明显,复现和避免也比较大略。
但笔者还想就这两个建议的缘故原由做进一步的探究,以是查阅了一些资料,大体的缘故原由如下:

第一个缘故原由,为什么不适宜做属性字段和方法参数?直白的说,便是麻烦。
为了引入Optional,却须要加入多段样板代码,比如判空操作。
使得在不得当的位置利用Optional不仅没有给我们带来便利,反而约束了写代码的逻辑。

写以下域模型代码

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic class Chestnut { private String firstName; private Optional<String> midName = Optional.empty(); private String lastName; public void setMidName(Optional<String> midName) { this.midName = midName; } public String getFullName() { String fullName = firstName; if(midName != null) { if(midName.isPresent()){ fullName = fullName.concat("." + midName.get()); } return fullName.concat("." + lastName); } }}

可见在setter方法中没有对形参做相应的校验,那么则须要在利用的getFullName()方法中,增加对属性midName的判空操作,由于完备可能通过setter方法使得属性为null。
如果把判空移到setter方法中,也并没有减少判空,使得平白挤进了一段样板代码。
其余在传入方法时,也须要对原来的value包装一层后再传入,使得代码显得累赘了,如下:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonchest.setMidName(Optional.empty());chest.setMidName(Optional.of("阿栗"));

在属性不为Optional的时候,如果给属性赋值,须要利用“消费”操作,比如orElse(),取出值再赋给属性,比较直接传入String类型的值作为字段和形参可以减少这些步骤,后者反而更加得当。

第二个缘故原由,为什么没有实现序列化?干系可以拜会Java Lamda的专家组谈论。

JDK在序列化上比较分外,须要同时兼顾向前和向后兼容,比如在JDK7中序列化的工具该当能够在JDK8中反序列化,反之亦然。
并且,序列化依赖于工具的identity保持唯一性。
当前Optional是引用类型的,但其被标记为value-based class(基于值的类),并且有操持在今后的某一个JDK版本中实现为value-based class,可见上图。
如果被设计为可序列化,就将涌现两个抵牾点:1)如果Optional可序列化,就不能将Optional实现为value-based class,而必须是引用类型,2)否则将value-based class加入同一性的敏感操作(包含引用的相等性如:==,同一性的hashcode或者同步等),但是这个与当前已发布的JDK版本都是冲突的。
以是综上,考虑到未来JDK的方案和实现的冲突,一开始就将Optional设置为不可序列化的,该当是最得当的方案了。

Value-Based Classes(基于值的类),以下是来自Java doc的阐明:

Value-based Classes

Some classes, such as java.util.Optional and java.time.LocalDateTime, are value-based. Instances of a value-based class:

1、are final and immutable (though may contain references to mutable objects);

2、have implementations of equals, hashCode, and toString which are computed solely from the instance's state and not from its identity or the state of any other object or variable;

3、make no use of identity-sensitive operations such as reference equality (==) between instances, identity hash code of instances, or synchronization on an instances's intrinsic lock;

4、are considered equal solely based on equals(), not based on reference equality (==);

5、do not have accessible constructors, but are instead instantiated through factory methods which make no committment as to the identity of returned instances;

6、are freely substitutable when equal, meaning that interchanging any two instances x and y that are equal according to equals() in any computation or method invocation should produce no visible change in behavior.

A program may produce unpredictable results if it attempts to distinguish two references to equal values of a value-based class, whether directly via reference equality or indirectly via an appeal to synchronization, identity hashing, serialization, or any other identity-sensitive mechanism. Use of such identity-sensitive operations on instances of value-based classes may have unpredictable effects and should be avoided.

2)Best practice

实践中常常组合利用以上各种方法,且很多方法常与Lambda表达式结合,获取想要的结果,这里列出两个常见的利用办法,值类型转换和凑集元素过滤。

a、示例一

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonChestnut chestnut = new Chestnut("锥栗板栗");if(chestnut != null){ String chestName = chestnut.getName(); if(chestName != null){ return chestName.concat("好好吃!
"); }}else{ return null;}

可以简化成:

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonChestnut chestnut = new Chestnut("锥栗板栗");return Optional.ofNullable(chestnut) .map(Chestnut::getName) .map(name->name.concat("好好吃!
")) .orElse(null);

b、示例二

PlainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPythonpublic static void main(String[] args) { // 创建一个栗子凑集 List<Chestnut> chestList = new ArrayList<>(); // 创建几个栗子 Chestnut chest1 = new Chestnut("abc"); Chestnut chest2 = new Chestnut("efg"); Chestnut chest3 = null; // 将栗子加入凑集 chestList.add(chest1); chestList.add(chest2); chestList.add(chest3); // 创建用于存储栗子名的凑集 List<String> nameList = new ArrayList(); // 循环栗子列表获取栗子信息,值获取不为空且栗子名以‘a’开头 // 如果不符合条件就设置默认值,末了将符合条件的栗子名加入栗子名凑集 for (Chestnut chest : chestList) { nameList.add(Optional.ofNullable(chest) .map(Chestnut::getName) .filter(value -> value.startsWith("a")) .orElse("未填写")); } // 输出栗子名凑集中的值 System.out.println("通过 Optional 过滤的凑集输出:"); nameList.stream().forEach(System.out::println);}

5、总结

本文首先,从所办理的问题开始,先容了当前NPE处理所碰着的问题;然后,分类地先容了Optional类中的方法并给出相应的示例;接着,从源码层面对几个常用的方法进行了比拟;末了,列举出了几个常见的误用形式和Best practice,结束了全文。

Optional类具有:可以显式表示值可能为空的语义和隐蔽可能存在空指针的不愿定性的优点,但是同时也具有,适用范围不是很广(建议利用于返回值和NPE逻辑处理)以及利用时须要更多考量的缺陷。

但是总体看来,Optional类是伴随Java8函数式编程涌现的一项新特性。
为处理逻辑的实现供应了更多的选择,未来期待更多的实践和best practice涌现,为Optional带来更多出场的机会。

6、参考

[1] https://mp.weixin.qq.com/s/q_WmD3oMvgPhakiPLAq-CQ 来源:wechat

[2] https://www.runoob.com/java/java8-optional-class.html 来源:菜鸟教程

[3] https://blog.csdn.net/qq_40741855/article/details/103251436 来源:CSDN

[4] https://yanbin.blog/java8-optional-several-common-incorrect-usages/#more-8824 来源:blog

[5] https://www.javaspecialists.eu/archive/Issue238-java.util.Optional---Short-Tutorial-by-Example.html 来源:java specialists

[6] Java核心技能 卷II - Java8的流库 - Optional类型

[7] https://www.zhihu.com/question/444199629/answer/1729637041 来源:知乎

如有不当之处,望示正~

作者:历子谦

相关文章

微信第三方登录便捷与安全的完美融合

社交平台已成为人们日常生活中不可或缺的一部分。微信作为我国最受欢迎的社交软件之一,拥有庞大的用户群体。为了方便用户在不同平台间切换...

网站建设 2025-02-18 阅读0 评论0

广东高速代码表解码高速公路管理智慧

高速公路作为国家交通动脉,连接着城市与城市,承载着巨大的物流和人流。广东作为我国经济大省,高速公路网络密布,交通流量巨大。为了更好...

网站建设 2025-02-18 阅读0 评论0