null
引用在历史上被引入到程序设计语言中,目的是为了表示变量值的缺失。
Java 8
中引入了一个新的类java.util.Optional
,对存在或缺失的变量值进行建模。
null带来的种种问题
- 它是错误之源。
NullPointerException
是目前Java
程序开发中最典型的异常。 - 它会使你的代码膨胀。它让你的代码充斥着深度嵌套的
null
检查,代码的可读性糟糕透顶。 - 它自身是毫无意义的。
null
自身没有任何的语义,尤其是,它代表的是在静态类型语言中以一种错误的方式对缺失变量值的建模。 - 它破坏了
Java
的哲学。Java
一直试图避免让程序员意识到指针的存在,唯一的例外是:null
指针。 - 它在
Java
的类型系统上开了个口子。null
并不属于任何类型,这意味着它可以被赋值给任意引用类型的变量。这会导致问题,原因是当这个变量被传递到系统中的另一个部分后,你将无法获知这个null
变量最初的赋值到底是什么类型。
public class Person {
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 getCarInsuranceName(Person person) {
return person.getCar().getInsurance().getName();
}
这段代码看起来相当正常,但是现实生活中很多人没有车。所以调用
getCar
方法的结果会怎样呢?在实践中,一种比较常见的做法是返回一个null
引用,表示该值的缺失,即用户没有车。而接下来,对getInsurance
的调用会返回null
引用的insurance
,这会导致运行时出现一个NullPointerException
,终止程序的运行。但这还不是全部。如果返回的person
值为null
会怎样?如果getInsurance
的返回值也是null
,结果又会怎样?怎样做才能避免这种不期而至的
NullPointerException
呢?Java 8
中引入了一个新的类java.util.Optional
。这是一个封装Optional
值的类。举例来说,使用新的类意味着,如果你知道一个人可能有也可能没有车,那么Person
类内部的car
变量就不应该声明为Car
,遭遇某人没有车时把null
引用赋值给它,而是应该像下图那样直接将其声明为Optional
类型。文章图片
变量存在时,
Optional
类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空”的Optional
对象,由方法Optional.empty()
返回。Optional.empty()
方法是一个静态工厂方法,它返回Optional
类的特定单一实例。null
引用和Optional.empty()
有什么本质的区别吗?从语义上,你可以把它们当作一回事儿,但是实际中它们之间的差别非常大:如果你尝试解引用一个null
,一定会触发NullPointerException
,不过使用Optional.empty()
就完全没事儿,它是Optional
类的一个有效对象,多种场景都能调用,非常有用。使用
Optional
而不是null
的一个非常重要而又实际的语义区别是,第一个例子中,我们在声明变量时使用的是Optional
类型,而不是Car
类型,这句声明非常清楚地表明了这里发生变量缺失是允许的。与此相反,使用Car
这样的类型,可能将变量赋值为null
,这意味着你需要独立面对这些,你只能依赖你对业务模型的理解,判断一个null
是否属于该变量的有效范畴。牢记上面这些原则,现在可以使用
Optional
类对前面的代码进行重构,结果如下。public class Person {
private Optional car;
//人可能有车,也可能没有车,因此将这个字段声明为Optional
public Optional getCar() {
return car;
}
} public class Car {
private Optional insurance;
//车可能进行了保险,也可能没有保险,所以将这个字段声明为Optional
public Optional getInsurance() {
return insurance;
}
}public class Insurance {
private String name;
//保险公司必须有名字
public String getName() {
return name;
}
}
代码中
person
引用的是Optional
,而car
引用的是Optional
,这种方式非常清晰地表达了你的模型中一个person
可能拥有也可能没有car
的情形,同样,car
可能进行了保险,也可能没有保险。与此同时,我们看到
insurance
公司的名称被声明成String
类型,而不是Optional
,这非常清楚地表明声明为insurance
公司的类型必须提供公司名称。使用这种方式,一旦解引用insurance
公司名称时发生NullPointerException
,你就能非常确定地知道出错的原因,不再需要为其添加null
的检查,因为null
的检查只会掩盖问题,并未真正地修复问题。insurance
公司必须有个名字,所以,如果你遇到一个公司没有名称,你需要调查你的数据出了什么问题,而不应该再添加一段代码,将这个问题隐藏。在你的代码中始终如一地使用
Optional
,能非常清晰地界定出变量值的缺失是结构上的问题,还是你算法上的缺陷,抑或是你数据中的问题。另外,引入Optional
类的意图并非要消除每一个null
引用。与此相反,它的目标是帮助你更好地设计出普适的API
,让程序员看到方法签名,就能了解它是否接受一个Optional
的值。这种强制会让你更积极地将变量从Optional
中解包出来,直面缺失的变量值。应用Optional的几种模式 创建Optional对象 使用
Optional
之前,你首先需要学习的是如何创建Optional
对象。完成这一任务有多种方法。可以使用静态工厂方法
Optional.empty
、Optional.of
以及Optional.ofNullable
创建Optional
对象。1.声明一个空的Optional
可以通过静态工厂方法
Optional.empty
,创建一个空的Optional
对象:Optional optCar = Optional.empty();
2.依据一个非空值创建Optional
还可以使用静态工厂方法
Optional.of
,依据一个非空值创建一个Optional
对象:Optional optCar = Optional.of(car);
如果
car
是一个null
,这段代码会立即抛出一个NullPointerException
,而不是等到你试图访问car
的属性值时才返回一个错误。3.可接受
null
的Optional
最后,使用静态工厂方法
Optional.ofNullable
,你可以创建一个允许null
值的Optional
对象:Optional optCar = Optional.ofNullable(car);
如果
car
是null
,那么得到的Optional
对象就是个空对象。Optional
提供了一个get
方法,它能非常精准地获取Optional
变量中的值。不过get
方法在遭遇到空的Optional
对象时也会抛出异常,所以不按照约定的方式使用它,又会让我们再度陷入由null
引起的代码维护的梦魇。因此,我们首先从无需显式检查的Optional
值的使用入手,这些方法与Stream
中的某些操作极其相似。Optional
类支持多种方法,比如map
、flatMap
、filter
,它们在概念上与Stream
类中对应的方法十分相似。使用map从Optional对象中提取和转换值 从对象中提取信息是一种比较常见的模式。比如,你可能想要从
insurance
公司对象中提取公司的名称。提取名称之前,你需要检查insurance
对象是否为null
,代码如下所示:String name = null;
if(insurance != null){
name = insurance.getName();
}
为了支持这种模式,
Optional
提供了一个map
方法。它的工作方式如下:Optional optInsurance = Optional.ofNullable(insurance);
Optional> name = optInsurance.map(Insurance:: getName);
从概念上,这与我们之前看到的流的
map
方法相差无几。map
操作会将提供的函数应用于流的每个元素。可以把Optional
对象看成一种特殊的集合数据,它至多包含一个元素。如果Optional
包含一个值,那函数就将该值作为参数传递给map
,对该值进行转换。如果Optional
为空,就什么也不做。下图对这种相似性进行了说明,展示了把一个将正方形转换为三角形的函数,分别传递给正方形和Optional
正方形流的map
方法之后的结果。文章图片
这看起来挺有用,但是怎样才能应用起来,重构之前的代码呢?前文的代码里用安全的方式链接了多个方法。
public String getCarInsuranceName(Person person) {
return person.getCar().getInsurance().getName();
}
为了达到这个目的,我们需要求助
Optional
提供的另一个方法flatMap
。使用flatMap链接Optional对象 你可能会想利用
map
重写之前的代码,如下所示:Optional optPerson = Optional.of(person);
Optional> name =
optPerson.map(Person:: getCar)
.map(Car:: getInsurance)
.map( Insurance:: getName);
但这段代码无法通过编译。
optPerson
是Optional【Java|Java 8 学习笔记10——用Optional取代null】
类型的变量,调用map
方法应该没有问题。但getCar
返回的是一个Optional
类型的对象,这意味着map
操作的结果是一个Optional>
类型的对象。因此,它对getInsurance
的调用是非法的,因为最外层的optional
对象包含了另一个optional
对象的值,而它当然不会支持getInsurance
方法。下图说明了你会遭遇的嵌套式optional
结构。文章图片
所以,我们该如何解决这个问题呢?使用流时,
flatMap
方法接受一个函数作为参数,这个函数的返回值是另一个流。这个方法会应用到流中的每一个元素,最终形成一个新的流的流。但是flagMap
会用流的内容替换每个新生成的流。换句话说,由方法生成的各个流会被合并或者扁平化为一个单一的流。这里你希望的结果其实也是类似的,但是你想要的是将两层的optional
合并为一个。下图说明
flatMap
方法在Stream
和Optional
类之间的相似性。文章图片
这个例子中,传递给流的
flatMap
方法会将每个正方形转换为另一个流中的两个三角形。那么,map
操作的结果就包含有三个新的流,每一个流包含两个三角形,但flatMap
方法会将这种两层的流合并为一个包含六个三角形的单一流。类似地,传递给optional
的flatMap
方法的函数会将原始包含正方形的optional
对象转换为包含三角形的optional
对象。如果将该方法传递给map
方法,结果会是一个Optional
对象,而这个Optional
对象中包含了三角形;但flatMap
方法会将这种两层的Optional
对象转换为包含三角形的单一Optional
对象。1.使用Optional获取car的保险公司名称
下面看看如何应用
Optional
的map
和flatMap
方法。前面的示例用基于Optional
的数据模式重写之后,如下所示。public String getCarInsuranceName(Optional person) {
return person.flatMap(Person:: getCar)
.flatMap(Car:: getInsurance)
.map( Insurance:: getName)
.orElse(" Unknown");
//如果Optional的结果值为空,设置默认值
}
通过比较这段代码和之前的代码,可以看到,处理潜在可能缺失的值时,使用
Optional
具有明显的优势。这一次,你可以用非常容易却又普适的方法实现之前你期望的效果——不再需要使用那么多的条件分支,也不会增加代码的复杂性。从具体的代码实现来看,首先修改了
getCarInsuranceName
方法的签名,因为我们很明确地知道存在这样的用例,即一个不存在的Person
被传递给了方法,比如,Person
是使用某个标识符从数据库中查询出来的,你想要对数据库中不存在指定标识符对应的用户数据的情况进行建模。你可以将方法的参数类型由Person
改为Optional
,对这种特殊情况进行建模。它通过类型系统让你的域模型中隐藏的知识显式地体现在你的代码中,换句话说,你永远都不应该忘记语言的首要功能就是沟通,即使对程序设计语言而言也没有什么不同。声明方法接受一个
Optional
参数,或者将结果作为Optional
类型返回,让你的同事或者未来你方法的使用者,很清楚地知道它可以接受空值,或者它可能返回一个空值。2.使用Optional解引用串接的Person/Car/Insurance对象
由
Optional
对象,可以结合使用之前介绍的map
和flatMap
方法,从Person
中解引用出Car
,从Car
中解引用出Insurance
,从Insurance
对象中解引用出包含insurance
公司名称的字符串。下图对这种流水线式的操作进行了说明。文章图片
这里,我们从以
Optional
封装的Person
入手,对其调用flatMap(Person:: getCar)
。如前所述,这种调用逻辑上可以划分为两步。第一步,某个Function
作为参数,被传递给由Optional
封装的Person
对象,对其进行转换。这个场景中,Function
的具体表现是一个方法引用,即对Person
对象的getCar
方法进行调用。由于该方法返回一个Optional
类型的对象,Optional
内的Person
也被转换成了这种对象的实例,结果就是一个两层的Optional
对象,最终它们会被flagMap
操作合并。从纯理论的角度而言,可以将这种合并操作简单地看成把两个Optional
对象结合在一起,如果其中有一个对象为空,就构成一个空的Optional
对象。如果对一个空的
Optional
对象调用flatMap
,实际情况又会如何呢?结果不会发生任何改变,返回值也是个空的Optional
对象。与此相反,如果Optional
封装了一个Person
对象,传递给flapMap
的Function
,就会应用到Person
上对其进行处理。这个例子中,由于Function
的返回值已经是一个Optional
对象,flapMap
方法就直接将其返回。第二步与第一步大同小异,它会将
Optional
转换为Optional
。第三步则会将Optional
转化为Optional
对象,由于Insurance.getName()
方法的返回类型为String
,这里就不再需要进行flapMap
操作了。截至目前为止,返回的
Optional
可能是两种情况:如果调用链上的任何一个方法返回一个空的Optional
,那么结果就为空,否则返回的值就是你期望的保险公司的名称。那么,如何读出这个值呢?毕竟你最后得到的这个对象还是个Optional
,它可能包含保险公司的名称,也可能为空。在上面的代码中,我们使用了一个名为orElse
的方法,当Optional
的值为空时,它会为其设定一个默认值。除此之外,还有很多其他的方法可以为Optional
设定默认值,或者解析出Optional
代表的值。默认行为及解引用Optional对象 采用
orElse
方法读取这个变量的值。使用这种方式你还可以定义一个默认值,遭遇空的Optional
变量时,默认值会作为该方法的调用返回值。Optional
类提供了多种方法读取Optional
实例中的变量值。get()
是这些方法中最简单但又最不安全的方法。如果变量存在,它直接返回封装的变量值,否则就抛出一个NoSuchElementException
异常。所以,除非你非常确定Optional
变量一定包含值,否则使用这个方法是个相当糟糕的主意。此外,这种方式即便相对于嵌套式的null
检查,也并未体现出多大的改进。orElse(T other)
是我们在上面代码中使用的方法,正如之前提到的,它允许你在Optional
对象不包含值时提供一个默认值。orElseGet(Supplier extends T> other)
是orElse
方法的延迟调用版,Supplier
方法只有在Optional
对象不含值时才执行调用。如果创建默认值是件耗时费力的工作,你应该考虑采用这种方式(借此提升程序的性能),或者你需要非常确定某个方法仅在Optional
为空时才进行调用,也可以考虑该方式(这种情况有严格的限制条件)。orElseThrow(Supplier extends X> exceptionSupplier)
和get
方法非常类似,它们遭遇Optional
对象为空时都会抛出一个异常,但是使用orElseThrow
你可以定制希望抛出的异常类型。ifPresent(Consumer super T>)
让你能在变量值存在时执行一个作为参数传入的方法,否则就不进行任何操作。
Person
和一个Car
对象,并以此为条件对外部提供的服务进行查询,通过一些复杂的业务逻辑,试图找到满足该组合的最便宜的保险公司:public Insurance findCheapestInsurance(Person person, Car car) {
//不同的保险公司提供的查询服务
//对比所有数据
return cheapestCompany;
}
还假设想要该方法的一个
null
-安全的版本,它接受两个Optional
对象作为参数,返回值是一个Optional
对象,如果传入的任何一个参数值为空,它的返回值亦为空。Optional
类还提供了一个isPresent
方法,如果Optional
对象包含值,该方法就返回true
,所以你的第一想法可能是通过下面这种方式实现该方法:public Optional nullSafeFindCheapestInsurance(
Optional person, Optional car) {
if (person.isPresent() && car.isPresent()) {
return Optional.of(findCheapestInsurance(person.get(), car.get()));
}else{
return Optional.empty();
}
}
这个方法具有明显的优势,我们从它的签名就能非常清楚地知道无论是
person
还是car
,它的值都有可能为空,出现这种情况时,方法的返回值也不会包含任何值。但该方法的具体实现和你之前曾经实现的null
检查太相似了:方法接受一个Person
和一个Car
对象作为参数,而二者都有可能为null
。利用Optional
类提供的特性,有没有更好或更地道的方式来实现这个方法呢?可以像使用三元操作符那样,无需任何条件判断的结构,以一行语句实现该方法,代码如下。
public Optional nullSafeFindCheapestInsurance(
Optional person, Optional car) {
return person.flatMap( p -> car.map(c -> findCheapestInsurance(p, c)));
}
这段代码中,对第一个
Optional
对象调用flatMap
方法,如果它是个空值,传递给它的Lambda
表达式不会执行,这次调用会直接返回一个空的Optional
对象。反之,如果person
对象存在,这次调用就会将其作为函数Function
的输入,并按照与flatMap
方法的约定返回一个Optional
对象。这个函数的函数体会对第二个Optional
对象执行map
操作,如果第二个对象不包含car
,函数Function
就返回一个空的Optional
对象,整个nullSafeFindCheapestInsuranc
方法的返回值也是一个空的Optional
对象。最后,如果person
和car
对象都存在,作为参数传递给map
方法的Lambda
表达式能够使用这两个值安全地调用原始的findCheapestInsurance
方法,完成期望的操作。Optional
类和Stream
接口的相似之处远不止map
和flatMap
这两个方法。还有第三个方法filter
,它的行为在两种类型之间也极其相似。使用filter剔除特定的值 你经常需要调用某个对象的方法,查看它的某些属性。比如,你可能需要检查保险公司的名称是否为“
Cambridge-Insurance
”。为了以一种安全的方式进行这些操作,你首先需要确定引用指向的Insurance
对象是否为null
,之后再调用它的getName
方法,如下所示:Insurance insurance = ...;
if(insurance != null && "CambridgeInsurance".equals(insurance.getName())){
System.out.println("ok");
}
使用
Optional
对象的filter
方法,这段代码可以重构如下:Optional optInsurance = ...;
optInsurance.filter(insurance ->
"CambridgeInsurance".equals(insurance.getName()))
.ifPresent(x -> System.out.println("ok"));
filter
方法接受一个谓词作为参数。如果Optional
对象的值存在,并且它符合谓词的条件,filter
方法就返回其值;否则它就返回一个空的Optional
对象。可以将Optional
看成最多包含一个元素的Stream
对象,这样这个方法的行为就非常清晰了。如果Optional
对象为空,它不做任何操作,反之,它就对Optional
对象中包含的值施加谓词操作。如果该操作的结果为true
,它不做任何改变,直接返回该Optional
对象,否则就将该值过滤掉,将Optional
的值置空。下表对
Optional
类中的方法进行了分类和概括。方法 | 描述 |
---|---|
empty | 返回一个空的Optional实例 |
filter | 如果值存在并且满足提供的谓词,就返回包含该值的Optional对象;否则返回一个空的Optional对象 |
flatMap | 如果值存在,就对该值执行提供的mapping函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象 |
get | 如果该值存在,将该值用Optional封装返回,否则抛出一个NoSuchElementException异常 |
ifPresent | 如果值存在,就执行使用该值的方法调用,否则什么也不做 |
isPresent | 如果值存在就返回true,否则返回false |
map | 如果值存在,就对该值执行提供的mapping函数调用 |
of | 将指定值用Optional封装之后返回,如果该值为null,则抛出一个NullPointerException异常 |
ofNullable | 将指定值用Optional封装之后返回,如果该值为null,则返回一个空的Optional对象 |
orElse | 如果有值则将其返回,否则返回一个默认值 |
orElseGet | 如果有值则将其返回,否则返回一个由指定的Supplier接口生成的值 |
orElseThrow | 如果有值则将其返回,否则抛出一个由指定的Supplier接口生成的异常 |
Java API
进行改动,让它们也使用Optional
,但这并不表示我们什么也做不了。可以在自己的代码中添加一些工具方法,修复或者绕过这些问题,让代码能享受Optional
带来的威力。下面会通过几个实际的例子讲解如何达到这样的目的。用Optional封装可能为null的值 现存
Java API
几乎都是通过返回一个null
的方式来表示需要值的缺失,或者由于某些原因计算无法得到该值。比如,如果Map
中不含指定的键对应的值,它的get
方法会返回一个null
。但是,大多数情况下,你可能希望这些方法能返回一个Optional
对象。你无法修改这些方法的签名,但是你很容易用Optional
对这些方法的返回值进行封装。我们接着用Map
做例子,假设你有一个Map
方法,访问由key
索引的值时,如果map
中没有与key
关联的值,该次调用就会返回一个null
。Object value = https://www.it610.com/article/map.get("key");
使用
Optional
封装map
的返回值,可以对这段代码进行优化。要达到这个目的有两种方式:可以使用笨拙的if-then-else
判断语句,毫无疑问这种方式会增加代码的复杂度;或者可以采用前文介绍的Optional.ofNullable
方法:Optional
每次你希望安全地对潜在为
null
的对象进行转换,将其替换为Optional
对象时,都可以考虑使用这种方法。异常与Optional的对比 由于某种原因,函数无法返回某个值,这时除了返回
null
,Java API
比较常见的替代做法是抛出一个异常。这种情况比较典型的例子是使用静态方法Integer.parseInt(String)
,将String
转换为int
。在这个例子中,如果String
无法解析到对应的整型,该方法就抛出一个NumberFormatException
。最后的效果是,String
无法转换为int
时,代码发出一个遭遇非法参数的信号,唯一的不同是,这次你需要使用try/catch
语句,而不是使用if
条件判断来控制一个变量的值是否非空。你也可以用空的
Optional
对象,对遭遇无法转换的String
时返回的非法值进行建模,这时你期望parseInt
的返回值是一个optional
。我们无法修改最初的Java
方法,但是这无碍我们进行需要的改进,你可以实现一个工具方法,将这部分逻辑封装于其中,最终返回一个我们希望的Optional
对象,代码如下所示。public static Optional stringToInt(String s) {
try {
//如果String能转换为对应的Integer,将其封装在Optioal对象中返回
return Optional.of(Integer.parseInt(s));
}catch (NumberFormatException e) {
//否则返回一个空的Optional对象
return Optional.empty();
}
}
建议是可以将多个类似的方法封装到一个工具类中,让我们称之为
OptionalUtility
。通过这种方式,以后就能直接调用OptionalUtility.stringToInt
方法,将String
转换为一个Optional
对象,而不再需要记得你在其中封装了笨拙的try/catch
的逻辑了。与
Stream
对象一样,Optional
也提供了类似的基础类型——OptionalInt
、OptionalLong
以及OptionalDouble
——所以上面代码中的方法可以不返回Optional
,而是直接返回一个OptionalInt
类型的对象。之前讨论过使用基础类型Stream
的场景,尤其是如果Stream
对象包含了大量元素,出于性能的考量,使用基础类型是不错的选择,但对Optional
对象而言,这个理由就不成立了,因为Optional
对象最多只包含一个值。不推荐使用基础类型的
Optional
,因为基础类型的Optional
不支持map
、flatMap
以及filter
方法,而这些却是Optional
类最有用的方法。此外,与Stream
一样,Optional
对象无法由基础类型的Optional
组合构成,所以,举例而言,如果前面的代码中返回的是OptionalInt
类型的对象,就不能将其作为方法引用传递给另一个Optional
对象的flatMap
方法。把所有内容整合起来 为了展示之前介绍过的
Optional
类的各种方法整合在一起的威力,假设需要向程序传递一些属性。为了举例以及测试开发的代码,创建了一些示例属性,如下所示:Properties props = new Properties();
props.setProperty("a", "5");
props.setProperty("b", "true");
props.setProperty("c", "-3");
现在,假设程序需要从这些属性中读取一个值,该值是以秒为单位计量的一段时间。由于一段时间必须是正数,想要该方法符合下面的签名:
public int readDuration(Properties props, String name)
即,如果给定属性对应的值是一个代表正整数的字符串,就返回该整数值,任何其他的情况都返回
0
。为了明确这些需求,可以采用JUnit
的断言,将它们形式化:assertEquals(5, readDuration(param, "a"));
assertEquals(0, readDuration(param, "b"));
assertEquals(0, readDuration(param, "c"));
assertEquals(0, readDuration(param, "d"));
这些断言反映了初始的需求:如果属性是
a
,readDuration
方法返回5
,因为该属性对应的字符串能映射到一个正数;对于属性b
,方法的返回值是0
,因为它对应的值不是一个数字;对于c
,方法的返回值是0
,因为虽然它对应的值是个数字,不过它是个负数;对于d
,方法的返回值是0
,因为并不存在该名称对应的属性。让我们以命令式编程的方式实现满足这些需求的方法,代码如下所示。public int readDuration(Properties props, String name) {
String value = https://www.it610.com/article/props.getProperty(name);
if (value!= null) {//确保名称对应的属性存在
try {
int i = Integer.parseInt(value);
//将String属性转换为数字类型
if (i> 0) { //检查返回的数字是否为正数
return i;
}
} catch (NumberFormatException nfe) { }
}
return 0;
//如果前述的条件都不满足,返回0
}
你可能已经预见,最终的实现既复杂又不具备可读性,呈现为多个由
if
语句及try/catch
块构成的嵌套条件。运用异常与Optional的对比部分的代码中提供的工具方法,可以通过一条精炼的语句重构上面代码中的方法。
如果需要访问的属性值不存在,
Properties.getProperty(String)
方法的返回值就是一个null
,使用ofNullable
工厂方法非常轻易地就能把该值转换为Optional
对象。接着,可以向它的flatMap
方法传递异常与Optional的对比部分的代码中实现的OptionalUtility.stringToInt
方法的引用,将Optional
转换为Optional
。最后,非常轻易地就可以过滤掉负数。这种方式下,如果任何一个操作返回一个空的Optional
对象,该方法都会返回orElse
方法设置的默认值0
;否则就返回封装在Optional
对象中的正整数。下面就是这段简化的实现:public int readDuration(Properties props, String name) {
return Optional.ofNullable(props.getProperty(name))
.flatMap(OptionalUtility:: stringToInt)
.filter(i -> i > 0)
.orElse(0);
}
使用
Optional
和Stream
时的那些通用模式都是对数据库查询过程的反思,查询时,多种操作会被串接在一起执行。使用
Optional
会迫使你更积极地解引用Optional
对象,以应对变量值缺失的问题,最终,你能更有效地防止代码中出现不期而至的空指针异常。使用
Optional
能帮助你设计更好的API
,用户只需要阅读方法签名,就能了解该方法是否接受一个Optional
类型的值。推荐阅读
- Java|Java基础——数组
- 人工智能|干货!人体姿态估计与运动预测
- java简介|Java是什么(Java能用来干什么?)
- Java|规范的打印日志
- Linux|109 个实用 shell 脚本
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- Spring注解驱动第十讲--@Autowired使用
- SqlServer|sql server的UPDLOCK、HOLDLOCK试验
- jvm|【JVM】JVM08(java内存模型解析[JMM])
- 技术|为参加2021年蓝桥杯Java软件开发大学B组细心整理常见基础知识、搜索和常用算法解析例题(持续更新...)