学会在Java中使用Optional功能
目录
- 前言
- Nullity
- Optional Class
- 客户责任
- null Optional Objects
- 重要方法
- 创建方法
- of
- ofNullable
- empty
- 实例方法
- isPresent&isEmpty
- get
- orElse系列
- orElseThrow系列
- ifPresent系列
- map
- flatMap
- filter
- 何时使用
- 返回值
- 字段
- 参数
- 替代方案
- null
- 空对象
- 例外情况
- 结论
前言
尽管存在争议,但Optiont极大地改进了Java应用程序的设计。在本文中,我们将了解如何、何时以及在哪里最好地应用Optional。
Optional
类的引入是Java语言设计的重大改进,但这一改进一直存在争议。在Optional
类之前,许多开发人员使用null
或异常来表示何时不存在所需的值;然而,使用Optional
类可以让我们明确说明何时可能存在或不存在值。尽管有这种改进,但Optional
类可能会不恰当地应用,弊大于利。
在本文中,我们将研究Optional
类的基本原理,包括:
- 其引入背后的目的和思维过程
Optional
课程中包含的基本方法- 使用
Optional
课程的适当时间和地点 - 一些可以替代的技术
Java,像大多数面向对象(OO)语言一样,对它的类型系统有一个秘密的后门。例如,假设我们有以下方法签名:
public Foo doSomething();
显然,此方法返回类型为Foo
的对象,但它也可以返回另一个值:null
。由于任何非原始类型都可以设置为null
,因此没有什么可以阻止开发人员编写以下方法实现:
public Foo doSomething() { return null; }
Nullity
这为调用此方法的客户端造成了一个繁琐的情况。在使用从doSomething
方法返回的Foo
对象之前,客户端必须首先检查该对象是否为null
:
Foo foo = doSomething(); if (foo == null) { // handle null case... } else { // do something with the foo object... }
这种方法确保了Foo对象的使用不会导致NullPointerException
(NPE)。然而,这又产生了另一个问题:任何非原语对象都可以隐式地设置为null
。因此,在使用方法之前,我们必须彻底检查方法返回的每个对象是否为空。不用说,这不仅在大型应用程序中是不可行的,而且还会混淆代码的清晰度。例如,为null
检查添加额外的行会在整个应用程序中引入样板代码,使Foo对象的使用变得不那么清晰(隐藏在if-else
语句中)。
根本问题是,我们不知道方法何时打算返回null
——例如当找不到所需的值时——或者可以根据要求保证永远不会返回null
。由于我们不确定,我们被迫假设任何返回的对象都可以为null
。
一个常用的解决方案是记录使用JavaDocs
返回值可以为null
。虽然这是对原始问题的改进,但这并不能确保客户端在使用对象之前检查空性(即Java编译器将毫无怨无故地编译代码,而无需进行这些null
检查)。同样,像@NotNull
注释也存在,但这些注释与文档方法存在相同的缺点。也就是说,可以规避执法。
Optional Class
相反,我们需要一种机制,允许方法开发人员显式表示方法的返回值可能存在,也可能不存在。该机制以[Optional
]类的形式在Java开发工具包(JDK)7中引入。该类充当可能不存在的对象的包装器。因此,如果我们知道我们的doSomething
方法可能不会返回所需的Foo
对象,我们可以将签名更改为:
public Optional<Foo> doSomething();
正如我们将在以下各节中看到的,Optional
提供了一套方法——许多功能性——允许客户端在不存在所需值时决定该怎么做。例如,当找不到所需的值时,我们可以使用orElse
方法返回默认值(在Optional
词典中称为空的Optional
)
Foo foo = doSomething().orElse(new Foo());
同样,当Optional
使用orElseThrow
方法为空时,我们也可以抛出异常:
Foo foo = doSomething().orElseThrow(SomeException::new);
重要的是要注意两件事:
- Java编译器迫使我们处理空
Optional
值的情况 - 客户负责处理缺失的期望值
虽然文档和注释确实将我们推向了正确、更明确的方向,但它们不允许我们将检查缺失值的责任强加给客户。另一方面,Optional
对象要求客户端决定如何处理缺失的值。
例如,以下内容不会编译:
Foo foo = doSomething();
相反,我们将看到一个编译错误,提醒我们,类型为Optional<Foo>
的对象不能转换为Foo
,因为doSomething
的返回类型是Optional<Foo>
,而foo
的类型是Foo
。因此,我们必须调用orElse
或orElseThrow
等方法——或get
,但我们稍后会看到为什么这不应该是首选——以便将Optional<Foo>
对象转换为Foo
对象。由于这两种方法都需要一个参数,因此,它们要求我们明确决定使用什么默认值,或者如果Optional
为空,则抛出什么异常。
客户责任
这让我们注意到(2):处理空的Optional
的责任在于客户。从本质上讲,doSomething
方法——本质上返回Optional<Foo>
而不是Foo
——告诉客户端,可能无法找到结果。因此,当找不到结果时,客户端现在负责处理案件(即必须调用其中一种Optional
方法,如orElse
,才能从Optional<Foo>
到Foo
)。
这种对客户端负责的方法意味着方法开发人员没有足够的信息来确定在缺少值的情况下应该做什么。当找不到值时,开发人员可以抛出异常,但缺失的值可能不是需要例外的情况(即它可能不是例外情况)。
例如,如果我们想检查一个对象是否已经存在,或者如果没有,创建一个,那么不存在的对象不会是一个错误,抛出异常是没有道理的。此外,抛出异常需要我们在调用代码中捕获异常,以创建默认值。
例如,假设我们创建以下方法:
public Foo findIfExists() throws FooNotFoundException;
要使用现有值,或者如果不存在,则创建默认值,我们必须执行以下操作:
Foo foo = null; try { foo = findIfExists(); } catch (FooNotFoundException e) { foo = // create default value... }
相反,我们可以从findIfExists
返回Optional
值:
public Optional<Foo> findIfExists();
然后,我们可以简单地使用orElse
方法来提供默认值:
Foo foo = findIfExists().orElse(/* create default value... */);
另外,后一种方法可读性更强。通过简单地阅读代码,我们知道这两行意味着查找是否存在或使用该值。在前一种情况下,当findIfExists
无法找到现有值时,我们必须有意识地将catch
子句的含义派生为默认值。因此,Optional
词汇比异常方法更接近预期的含义。
了解这一点,当客户端负责处理丢失的对象而缺少对象不是错误时,Optional
是一种有用的技术。有时,缺失的值可能是错误——例如,当我们假设一个值存在,而其缺失可能会给应用程序带来致命结果时——方法应该抛出一个选中或未检查的异常。然而,在某些情况下(例如findIfExists
方法),缺席对象不是错误,使用Optional
是确保客户端显式处理缺失对象的有效方法。
null Optional Objects
必须解决一个警告:Optional
对象可能是null
的。由于Optional
对象和Foo
一样是非原始对象,因此它们也可以设置为null
。例如,doSomething
的以下实现将编译无错误:
public Optional<Foo> doSomething() { return null;
这将导致客户端必须同时处理null
返回值和处理空Optional
情况的奇怪情况:
Optional<Foo> possibleFoo = doSomething(); if (possibleFoo == null) { // handle missing object case... } else { Foo foo = possibleFoo.orElse(/* handle missing object case... */); }
这不仅为丢失的对象情况带来了重复(即在两个位置处理丢失的对象的情况),而且还重新引入了清晰度降低的问题。相反,当从方法返回Optional
值时,我们不应该检查null
值。根据Optional类文档:
类型为Optional的变量本身永远不应该null;它应该始终指向Optional实例。
如果返回null
值代替Optional
对象,则方法开发人员违反了方法规定。通过声明方法将返回Optional
对象,方法开发人员还声明返回null
无效。由于Optional
对象表示对象丢失的可能性,因此null
值没有有效的用例(即方法在所有情况下都应该返回空的Optional
而不是null
)。
因此,每当我们处理Optional
对象时,我们正确地假设Optional
对象永远不会为null
。虽然Optional
对象在实践中可能是null
的,但这个问题应该由方法开发人员而不是客户端解决。
重要方法
通过了解Optional
类背后的概念,我们现在可以看看如何在实践中使用Optional
对象。Optional
类包含大量方法,可以分为两类:创建方法和实例方法。
创建方法
Optional
创建方法是静态方法,允许我们创建各种Optional
对象来满足我们的需求。目前有三种这样的方法:一种用于创建填充的Optional
(即包装值不是null
的Optional),一种用于创建填充或空的Optional
,一种用于创建空的Optional
。
of
方法of
静态允许我们用Optional
对象包装现有对象。如果现有对象不是null
,则返回填充的Optional
:
Optional<Foo> foo = Optional.of(new Foo());
如果现有对象为null
,则抛出NPE:
Optional<Foo> foo = Optional.of(null); // 抛出 NullPointerException
ofNullable
当传递非null
值时, ofNullable
静态方法与方法相同(即生成填充的Optional
),但在传递null
时将生成空Optional
(即不会抛出NPE):
Optional<Foo> foo1 = Optional.ofNullable(new Foo()); // populated Optional Optional<Foo> foo2 = Optional.ofNullable(null); // null Optional
当对象的空性未知时,通常使用这种方法。
empty
empty
静态方法只需创建一个空的Optional
:
Optional<Foo> foo = Optional.empty(); // empty
根据定义,这种方法与以下方法相同:
Optional<Foo> foo = Optional.ofNullable(null);
正如我们将在下面几节中看到的,empty
通常在已知没有值存在的情况下使用。
实例方法
实例方法允许我们与现有的Optional
对象交互,并主要专注于查询Optional
对象的状态,从Optional
对象获取包装对象,并操作Optional
对象。
isPresent&isEmpty
Optional
类中包含两种查询方法,允许我们检查给定的Optional
是填充的还是空的:
isPresent
:如果填充了Optional
,则返回true
,否则返回false
isEmpty
:如果Optional
为empty
,则返回true
,否则返回false
因此,给定填充的Optional
,查询方法将返回以下内容:
Optional<Foo> populated = // ...populated Optional... populated.isPresent(); // true populated.isEmpty(); // false
给定一个空的Optional
,查询方法将返回以下内容:
Optional<Foo> empty = // ...empty Optional... populated.isPresent(); // false populated.isEmpty(); // true
get
如果Optional
被填充,get
方法将获得由Optional
包装的值,如果Optional
为空,则抛出NoSuchElementException。当我们可以保证填充Optional时,此方法可用于将现有Optional
转换为其值(即从Optional<Foo>
转换为Foo
),但我们应该谨慎使用此方法。
在实践中,保证填充Optional
需要我们首先使用isPresent
或isEmpty
方法查询Optional
,然后调用get
:
Optional<Foo> possibleFoo = doSomething(); if (possibleFoo.isPresent()) { Foo foo = possibleFoo.get(); // ...use the foo object... } else { // ...handle case of missing Foo... }
这种模式的问题在于,这与我们在引入Optional
之前执行的null
检查非常相似。因此,这种方法消除了Optional
类的固有好处。在大多数情况下,我们应该避免使用get
方法,并使用其他方法之一(如orElse
或orElseThrow
)来获取与填充的Optional
值相关联。
orElse系列
orElse
系列方法允许我们获得由Optional
包装的值(如果填充了Optional
),或者如果Optional
为空,则获取默认方法。这个系列中最简单的方法是orElse
,它接受包装类型的对象,如果Optional
是空的,则返回它。例如,给定anOptionalOptional<Foo>
对象,orElse
方法接受一个Foo
对象。如果填充了Optional
,它会返回填充值;如果Optional
为空,则返回我们传递给orElse
方法的Foo
对象:
Optional<Foo> possibleFoo = doSomething(); Foo foo = possibleFoo.orElse(new Foo());
然而,有时创建默认值可能是一项昂贵的操作,并且不太可能使用。例如,默认值可能需要建立到远程服务器的连接,或者可能需要从数据库进行扩展或大型查找。如果可能填充Optional
,我们不太可能需要默认值。使用orElse
方法,即使未使用,我们也被迫创建默认值,这可能会导致严重的性能影响。
Optional
类还包括orElseGet
方法,该方法采用可以懒散创建默认对象的Supplier。这允许Optional
类仅在需要时创建默认对象(即仅在Optional
为空时创建默认对象)。
例如:
Optional<Foo> possibleFoo = doSomething(); Foo foo = possibleFoo .orElseGet(() -> { /* ...lazily create a Foo object... */ });
orElseThrow系列
与orElse
方法类似,Optional
类提供了一个orElseThrow
方法,如果Optional
为空,则允许我们在获取包装值时抛出异常。然而,与orElse
方法不同,orElseThrow
方法有两种形式:
- 无参数形式,如果
Optional
为空,则抛出NoSuchElementException
,如果Optional
填充,则返回包装值 - 一个接受
Supplier
的表单,该Supplier
创建Throwable
对象,并在Optional
为空时抛出Throwable
对象,或在Optional
填充时返回包装好的值
例如,我们可以从Optional<Foo>
对象中获取Foo
对象,
如下所示:
Optional<Foo> possibleFoo = doSomething(); Foo foo = possibleFoo .orElseThrow();
如果Optional
是空的,将抛出NoSuchElementException
。如果填充了Optional
,则将返回包装值。因此,orElseThrow
方法的功能与get
方法相同,但它的名称更好地描述了其目的。因此,当Optional
未填充时,orElseThrow
方法应使用任何值的任何地方来抛出NoSuchElementException
,而无需首先检查它是否已填充(即不使用isPresent
或isEmpty
查询方法)。
get
方法仅当在Optional
查询方法之一中使用时才应保留用于(即首先选中Optional
的填充或空状态)。请注意,此orElseThrow
方法是在JDK 9中引入的,以减少围绕get
方法使用的混乱,应该比get方法更喜欢。
我们[在Java 8]中犯的少数错误之一是命名
Optional.get()
,因为这个名字只是邀请人们在不调用isPresent()
用它,首先破坏了使用Optional
的全部意义......
在Java 9时间范围内,我们建议弃用
Optional.get()
,但公众对此的反应是......比说冷。作为较小的一步,我们在[Java] 10中引入了orElseThrow()
...作为当前get()
有害行为的更透明的同义词。
Optional
类还包括一个重载的orElseThrow
方法,当Optional
为空时,该方法会抛出自定义异常。此方法接受创建任何Throwable
对象或Throwable
子类的对象的Suppler
并抛出它。例如:
Optional<Foo> possibleFoo = doSomething(); Foo foo = possibleFoo.orElseThrow(() -> { /* ...lazily create a Foo object... */ });
当客户端认为丢失的对象是一个错误,并希望在访问空的Optional
时抛出异常时,这非常有用。使用构造函数的功能形式抛出简单的异常也是一种常见的做法:
Optional<Foo> possibleFoo = doSomething(); Foo foo = possibleFoo.orElseThrow(SomeException::new);
ifPresent系列
如果Optional被填充,ifPresent方法接受一个Consumer,该Consumer使用包装的值执行操作。这是使用orElse或orElseThrow方法获得包装对象的函数替代,主要是当我们不希望在值不存在的情况下执行任何操作时。
例如:
Optional<Foo> possibleFoo = doSomething(); possibleFoo.ifPresent(foo -> { /* ...do something with foo... */ });
Optional
类还包括类似的方法,ifPresentOrElse
,允许我们在Optional
也是空时处理案例。ifPresentOrElse
方法接受的第一个参数是Consumer
,如果填充了Optional
,则使用包装值执行操作,而第二个参数是Runnable,如果Optional
为空,则执行操作。因此,只有当Optional
被填充时才会调用Consumer
,而只有当Optional
为空时,才会调用Runnable
。例如:
Optional<Foo> possibleFoo = doSomething(); possibleFoo.ifPresentOrElse( foo -> { /* ...do something with foo... */ }, () -> { /* ...do something when no foo found... */ }
这两种方法的好处是,如果Optional
为空,则永远不会调用Consumer
。同样,在ifPresentOrElse
的情况下,如果填充了Optional
,则永远不会调用Runnable
。这使我们能够提供复杂或昂贵的操作,这些操作将根据Optional
状态被懒惰地调用。
请注意,这种方法不应该仅仅用于昂贵的操作。每当对填充值执行操作时,都应使用此方法。例如,如果我们只想在对象存在的情况下更新它,我们可以做一些类似于以下内容的事情:
public class Bar { private boolean isUpdated = false; public void update() { isUpdated = true; } } public Optional<Bar> findBar() { // ...return a populated Bar if it could be found... } findBar().ifPresent(bar -> bar.update());
在这种情况下,如果找不到Bar
对象,我们不关心执行任何操作。如果我们是,我们可以改用ifPresentOrElse
方法。
map
如果填充了Optional
,map
方法允许我们将包装值从一个对象转换为另一个对象。这种方法可以被认为是一种管道方法,其中包装的值沿着管道传递并转换为新值。此方法的工作原理是接受应用于包装值的Function对象,以生成映射值。如果Optional
是空的,则永远不会调用Function
对象,并且从map
方法返回空的Optional
。
当我们不知道是否存在一个值时,这种方法非常有用,但如果存在,它应该转换为另一个对象。这是从数据库读取时常见的用例,数据库通常存储数据传输对象(DTO)。在大多数应用程序中,DTO用于有效地将域对象存储在数据库中,但在应用程序的更高级别上,需要域对象本身。因此,我们必须从DTO转换为域对象。
如果我们对数据库对象进行查找,我们可能会找到也可能找不到该对象。因此,这是返回Optional
包装DTO的好用例。为了转换为域对象,我们可以使用map
方法。例如,假设我们有一个DTO(PersonDto
),将Person
对象的名称存储在一行中,而Person
对象的名称分为名字和姓氏(即,该名称在PersonDto
对象中存储为"John Doe"
但它在Person
对象中以"John"
的名字和"Joe"
的姓氏存储)。我们可以使用映射器对象从PersonDto
转换为Person
对象,并使用映射器将从数据库返回的PersonDto
对象映射到Person
对象:
public class Person { private String firstName; private String lastName; // ...getters & setters... } public class PersonDto { private String name; // ...getters & setters... } public class PersonMapper { public Person fromDto(PersonDto dto) { String[] names = dto.getName().split("\s+"); Person person = new Person(); person.setFirstName(names[0]); person.setLastName(names[1]); return person; } } public class Database { public Optional<PersonDto> findPerson() { // ...return populated DTO if DTO is found... } } Database db = new Database(); PersonMapper mapper = new PersonMapper(); Optional<Person> person = db.findPerson() .map(mapper::fromDto);
注意,可能有一个转换会导致一个空的Optional
。例如,如果从给定对象到另一个对象的转换是不可能的,那么map
方法应该返回一个空的Optional
。执行这种技术的反模式是让Function
对象返回null
,然后用map
方法(使用ofNullable
,它允许我们的null
对象在不抛出异常的情况下被包装)包装到空的可选对象中:
Optional<Person> person = db.findPerson() .map(dto -> { if (dtoCanBeConverted()) { return mapper.fromDto(dto); } else { return null; } });
如果方法dtoCanBeConverted
计算为false
,则Function
对象返回null
,从而导致person
为空的Optional
。这种方法存在缺陷,因为它重新引入了null
值的隐式使用,其替换是Optional
类的原始目的。相反,我们应该使用flatMap
方法,并显式返回一个空的Optional
。
flatMap
flatMap
方法类似于map
方法,但flatMap
接受一个Function
对象,该函数将包装值转换为新的Optional
。与map
方法不同,flatMap
允许我们返回我们选择的Optional
。因此,如果映射Function
无法转换包装值,我们可以显式返回空的Optional
值:
Optional<Person> person = db.findPerson() .flatMap(dto -> { if (dtoCanBeConverted()) { Person person = return dao.fromDto(dto); return Optional.ofNullable(person); } else { return Optional.empty(); } });
需要注意的是,我们不再能够像使用map
方法那样简单地返回Person
对象。相反,我们现在负责将转换后的对象包装成Optional
。注意,如果Function
对象返回null Optional
,则抛出一个NPE
。例如,以下代码在执行时会抛出一个NPE
:
Optional<Person> person = db.findPerson() .flatMap(dto -> null);
filter
如果填充的Optional
满足提供的Predicate
,则filter
方法允许我们返回填充的Optional
。因此,如果filter
方法应用于空的Optional
,则不会调用Predicate
。同样,如果filter
方法应用于填充的Optional
,但包装值不满足提供的Predicate
(即Predicate
对象的test
方法计算false
),则返回一个空的Optional
。例如:
public class Bar { private int number; public Bar(int number) { this.number = number; } // ...getters & setters... } Predicate<Bar> greaterThanZero = bar -> bar.getNumber() > 0; Optional.of(new Bar(1)) .filter(greaterThanZero) .isPresent(); // true Optional.of(new Bar(-1)) .filter(greaterThanZero) .isPresent(); // false
何时使用
Optional
类最具争议的方面之一是何时应该和不应该使用它。在本节中,我们将研究一些常见的用例,例如方法返回值、字段和参数,其中Optional
可能非常适合也可能不合适。
返回值
正如我们在本文中看到的那样,Optional
值非常适合方法返回值,因为这是其预期目的。根据Optional类文档:
Optional
主要用于方法返回类型,其中明确需要表示“无结果”,并且使用null
可能会导致错误。
一般来说,在以下情况下,应使用Optional
作为返回值:
- 预计一个值可能存在,也可能不存在
- 如果缺少值,这不是错误
- 客户负责处理丢失价值的情况
Optional
返回值通常用于可能找到或找不到所需对象的查询。
例如,存储库通常将以以下方式定义:
public interface BookRepository { public Optional<Book> findById(long id); public Optional<Book> findByTitle(String title); // ... }
这允许客户端以适合调用方法的上下文的方式处理丢失的Book
对象,例如忽略丢失的对象、创建默认对象或抛出异常。
字段
虽然Optional
对象非常适合返回类型,但它们不太适合例如字段。可以创建一个类似于以下内容的字段,但这是非常不可取的:
public class Bar { private Optional<Foo> foo; // ...getters & setters... }
Optional
应避免字段,因为Optional
类不可序列化(即没有实现Serializable接口)。
当然,人们会做他们想做的事。但我们添加此功能时确实有明确的意图,这不是一个通用的目的,也许类型,就像许多人希望我们这样做一样。我们的意图是为库方法返回类型提供有限的机制,其中需要一种明确的方法来表示“无结果”,因此使用
null
极有可能导致错误。
因此,Optional
类型仅适用于方法返回类型。由于字段构成类的内部状态,外部客户端不应可见,如果字段被认为是可选的,则可以创建一个getter,返回Optional
对象:
public class Bar { private Foo foo; public Optional<Foo> getFoo() { return Optional.ofNullable(foo); } }
使用这种技术,客户会明确被告知foo
值可能存在,也可能不存在,同时保持Bar
的可序列化性。
参数
在有效的情况下,方法或构造函数的参数可能是可选的,但Optional
的不应用于此目的。
例如,应避免以下情况:
public class Bar { public void doSomething(Optional<Foo> foo) { // ... } }
不应将参数类型设置为Optional
,而应使用方法重载:
public class Bar { public void doSomething() { // ... } public void doSomething(Bar bar) { // ... } }
此外,具有不同方法名称的非过载方法也可以使用:
public class Bar { public void doSomething() { // ... } public void doSomethingWithBar(Bar bar) { // ... } }
替代方案
虽然Optional
类在正确的上下文中有效,但当可能找到或找不到所需的值时,它并不是可以使用的唯一方法。在本节中,我们涵盖了Optional
类的三种替代方案,以及如何在适当的上下文中应用它们。
null
最简单的替代方案是使用null
,正如我们在本文开头看到的那样。虽然这种方法确实实现了我们的目标,但在引入Optional
类后,仅当Optional
对象需要太多的开销时,才应使用null
。此开销可以是Optional
包装类的额外内存需求,也可以是执行Optional
方法所需的额外周期。
在Optional
更有效的情况下,人们很容易以性能为借口使用null
,但是在大多数应用程序中,Optional
类只增加了少量的开销。除非我们处理的是低级代码,就像来自网络或驱动程序的字节一样,或者我们处理的是极其大量的数据,否则对于方法返回类型来说,可选项应该总是优先于null
。
空对象
比null
值更有效的替代方案是引入空对象。null对象是扩展所需类型的对象,但包含本应为null
大小写执行的逻辑。例如,假设我们有以下代码:
public class Article { private long id; public void submit() { // ... } // ...getters & setters... } public class ArticleRepository { public Article findById(long id) { // ...return the article if it can be found... } } ArticleRepository repository = new ArticleRepository(); Article article = repository.findById(1); if (article == null) { throw new ArticleNotFoundException(); } else { article.submit(); }
我们可以使用空对象重构此代码到以下内容:
public class Article { // ...same as before... } public class NullArticle extends Article { @Override public void submit() { throw new ArticleNotFoundException(); } } public class ArticleRepository { public Article findById(long id) { if (articleIsFound()) { // return article... } else { return new NullArticle(); } } } ArticleRepository repository = new ArticleRepository(); Article article = repository.findById(1); article.submit();
需要注意的是,引入空对象假设方法本身知道如何处理缺失值的情况。
例外情况
我们在本文中看到的另一个替代方案是在找不到所需对象时抛出异常。如果方法知道未能找到所需的对象是一个错误,则此方法有效。
例如:
public class ArticleRepository { public Article findById(long id) { if (articleIsFound()) { // return article... } else { throw new ArticleNotFoundException(); } } }
结论
在许多情况下,所需的值可能存在于也可能不存在于应用程序中,以可读和有效的方式处理这些情况是精心设计的软件的重要组成部分。从JDK 7开始,Java包括Optional
类,该类允许开发人员返回可能存在也可能不存在的值,并允许客户端根据发生这些情况的上下文处理这些情况。虽然Optional
类只能用于方法返回值,但了解其有用性以及如何使用简单技术应用它是掌握现代Java的重要组成部分。
到此这篇关于学会在Java中使用Optional功能的文章就介绍到这了,更多相关Java使用Optional内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!