如何在Java中使用Optionals


Optional该类的引入是Java语言设计的一项重大改进,但是这种改进一直存在争议。在Optional上课之前,许多开发人员使用null或异常来表示何时不存在所需的值。Optional但是,使用该类可以使我们显式声明何时可以存在或不可以存在一个值。尽管有这种改进,但Optional该类仍可能不适当地使用,并且弊大于利。

在本文中,我们将研究Optional该类的基础知识,包括:

  • 引入的目的和思想过程
  • Optional该类中包含的基本方法
  • Optional上课的适当时间和地点
  • 可以替代使用的一些替代技术

Purpose 与大多数面向对象(OO)语言一样,Java对类型系统也有一个后门。例如,假设我们具有以下方法签名:

public Foo doSomething();

很明显,此方法返回一个类型的对象Foo,但它也可以返回另一个值:null。由于可以将任何非基本类型都设置为null,因此不会阻止开发人员编写以下方法实现:

public Foo doSomething() {
    return null; 
}

Nullity 这给调用此方法的客户端造成了麻烦的情况。在使用Foo从doSomething方法返回的对象之前,客户端必须首先检查该对象是否不是null:

Foo foo = doSomething();
if (foo == null) {
    // handle null case...
}
else {
    // do something with the foo object...
}

这种方法可确保使用该Foo对象不会导致NullPointerException(NPE)。不过,这会带来另一个问题:可以将ny 非基本对象隐式设置为null。因此,在使用它之前,我们必须详尽地检查从方法返回的每个对象是否为空。不用说,这不仅在大型应用程序中是不可行的,而且还会混淆我们代码的清晰度。例如,为null-check添加额外的行会在我们的整个应用程序中引入样板代码,从而使Foo对象的使用不太清晰(隐藏在if-else语句中)。

根本问题是我们不知道某个方法打算何时返回(null例如何时找不到所需的值),或者无法通过合同保证null永远不会返回。由于我们不确定,因此我们被迫假定任何 返回的对象都可以是null。

常用的一种解决方案是记录可以null使用JavaDocs返回值。尽管这是对原始问题的改进,但这不能确保客户端在使用对象之前检查是否为空(即Java编译器将在没有这些null检查的情况下编译代码-毫无怨言)。同样,也@NotNull存在类似的注释,但是它们具有与文档编制方法相同的缺点。即,可以规避执法。

Optional Class 相反,我们需要一种机制,该机制允许方法开发人员明确表示方法的返回值可能存在或可能不存在。Java开发工具包(JDK)7中以Optional类的形式介绍了此机制。此类充当可能不存在的对象的包装。因此,如果我们知道我们的doSomething方法可能不会返回所需的Foo对象,则可以将签名更改为:

public Optional<Foo> doSomething();

正如我们将在以下各节中看到的那样,Optional提供了一套方法(许多功能),使客户可以决定在不存在所需值时该怎么做。例如,orElse当找不到所需的值(在词典中称为空OptionalOptional)时,我们可以使用该方法返回默认值:

Foo foo = doSomething()
    .orElse(new Foo());

同样,当Optional使用空orElseThrow方法时,我们也可以抛出一个异常:

Foo foo = doSomething()
    .orElseThrow(SomeException::new);

重要的是要注意两件事:

Java编译器迫使我们处理空Optional值的情况 客户负责处理缺失的期望值 尽管文档和注释确实使我们朝着正确的(更明确的)方向前进,但它们不允许我们将检查缺失值的责任强加给客户。Optional另一方面,对象要求客户决定如何处理缺失值。

例如,以下内容将不会 编译:

Foo foo = doSomething();

取而代之的是,我们将看到一个编译错误,警告我们,由于is的返回类型和is的类型,Optional无法将类型的对象强制转换为。因此,为了将对象转换为对象,我们必须调用诸如或-或的方法,但是稍后我们将了解为什么不应该将其作为首选。由于这两种方法都需要一个参数,因此它们要求我们对要使用的默认值或如果为空时抛出的异常做出明确的决定。FoodoSomethingOptional<Foo>fooFooorElseorElseThrowgetOptional<Foo>FooOptional

Client Responsibility 这使我们注意到(2):处理空缺的责任Optional在于客户。从本质上讲,该doSomething方法(本质上是返回Optional而不是返回)Foo告诉客户有可能找不到结果。因此,当未找到结果时,客户端现在负责处理案件(即,必须调用Optional诸如之类的方法之一orElse才能从Optional<Foo>Foo)。

这种由客户负责的方法意味着方法开发人员没有足够的信息来确定在缺少值的情况下应采取的措施。当找不到该值时,开发人员可能会引发异常,但是缺少值可能不是保证例外的情况(即,可能不是例外 情况)。

例如,如果我们要检查一个对象是否已经存在,或者如果不存在则创建一个对象,那么不存在的对象将不是错误,并且也不会引发异常。此外,抛出异常将要求我们在调用代码中捕获该异常以创建默认值。例如,假设我们创建以下方法:

public Foo findIfExists() throws FooNotFoundException;

要使用现有值,或者如果不存在则创建默认值,我们必须执行以下操作:

Foo foo = null;
try {
    foo = findIfExists();
}
catch (FooNotFoundException e) {
    foo = // create default value...
}

我们可以改为Optional从中返回一个值findIfExists

public Optional<Foo> findIfExists();

然后,我们可以简单地使用该orElse方法来提供默认值:

Foo foo = findIfExists()
    .orElse(/* create default value... */);

此外,后一种方法更具可读性。通过简单地阅读代码,我们了解到这两行意味着找到是否存在或使用此值。在前一种情况下,catchfindIfExists找不到现有值时,我们必须有意识地将子句的含义推导为默认值。因此,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从方法返回an时,我们不应该检查null值。根据Optional课程文件:

类型Optional本身不应是的变量null; 它应该始终指向一个Optional实例。

如果null返回值代替Optional对象,则表示方法开发人员违反了方法合同。通过说明方法将返回Optional对象,方法开发人员还说明了return无效null。由于Optional对象表示可能丢失对象,因此没有有效的用例null值(即,该方法应返回空值,Optional而不是null在所有情况下)。

因此,每当我们处理一个Optional对象时,我们都应该假设该Optional对象永远不会是正确的null。尽管Optional对象可以null在实践中使用,但这是一个问题,应该由方法开发人员而不是客户端来解决。

Important Methods 了解了Optional类背后的概念之后,我们现在可以看看如何Optional在实践中使用对象。的Optional类中含有大量套件的方法,其可被细分为两类:创建方法和实例方法。

Creation MethodsOptional创建方法是静态方法,使我们能够创建各种Optional对象,以满足我们的需要。当前有三种这样的方法:一种用于创建填充Optional(即,Optional其包装值不是null)的方法,一种用于创建填充或为空的方法Optional,以及一种用于创建Empty的方法Optional。

of 静态of方法使我们可以使用来包装现有对象Optional。如果现有对象不是null,Optional则返回一个已填充的对象:

Optional<Foo> foo = Optional.of(new Foo());  // populated Optional

如果现有对象是null,则会引发NPE:

Optional<Foo> foo = Optional.of(null);      // throws NullPointerException

ofNullable 在ofNullable当非静态方法相同的方法的null传递值(即,填充的Optional产生),但是会产生一个空Optional时null被传递(即,NPE不会被抛出):

Optional<Foo> foo1 = Optional.ofNullable(new Foo());  // populated Optional
Optional<Foo> foo2 = Optional.ofNullable(null);       // empty Optional

当对象的无效性未知时,通常使用此方法。

emptyempty静态方法简单的创建一个空的Optional:

Optional<Foo> foo = Optional.empty();    // empty Optional

根据定义,此方法等同于:

Optional<Foo> foo = Optional.ofNullable(null);

正如我们将在以下各节中看到的那样,empty当先验地知道不存在任何值时,通常会使用该方法。

Instance Methods 实例方法使我们可以与现有Optional对象进行交互,并且主要集中于查询的状态,OptionalOptional和获取包装的对象以及操作Optional对象。

isPresent & isEmpty Optional该类中包含两个查询方法,使我们可以检查给定的Optional是填充的还是空的:

  1. isPresent:true如果Optional已填充,false则返回,否则返回
  2. isEmpty:true如果Optionalis为empty,false则返回,否则返回 因此,给定一个populated 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 如果填充get了,Optional则该方法获取由包装的值;如果Optional填充为空NoSuchElementException,Optional则抛出。当可以保证已填充时,此方法可用于将现有Optional值转换为其值(即从Optional<Foo>到Foo)Optional,但我们应谨慎使用此方法。

实际上,要确保Optional填充了一个,要求我们首先Optional使用isPresent或isEmpty方法查询,然后调用get:

Optional<Foo> possibleFoo = doSomething();
if (possibleFoo.isPresent()) {
    Foo foo = possibleFoo.get();
    // ...use the foo object...
}
else {
    // ...handle case of missing Foo...
}

这种模式的问题在于,它非常类似于null我们在引入之前执行的-check Optional。因此,这种方法消除了Optional该类的固有好处。在大多数情况下,我们应该避免使用该get方法,而应使用其他方法之一(例如orElse或)来orElseThrow获取与populated相关的值Optional。

orElse 家庭 这一orElse系列方法允许我们获得由Optional-(如果Optional填充了)或-作为默认方法(如果Optional为空)包装的值。该系列中最简单的方法是orElse,它采用包装类型的对象,如果的Optional值为空,则将其返回。例如,给定一个Optional<Foo>对象,该orElse方法接受一个Foo对象。如果Optional填充了,则返回填充的值;否则,返回0。如果的Optional值为空,则返回Foo我们传递给该orElse方法的对象:

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 Family

  1. 与该orElse方法类似,Optional该类提供了orElseThrow一种方法,该方法允许我们在获取包装的值(如果Optional值为空)时引发异常。orElse但是,与orElseThrow方法不同,该方法具有两种形式:

  2. No参数形式,NoSuchElementException如果的Optional值为空,则抛出,如果Optional填充的是,则返回包装的值 一种格式为a的表单,该表单Supplier创建Throwable对象,Throwable如果元素Optional为空,则抛出该对象;如果Optional填充了,则返回包装的值 例如,我们可以Foo从一个Optional<Foo>对象获取一个对象,如下所示:

Optional<Foo> possibleFoo = doSomething();
Foo foo = possibleFoo
    .orElseThrow();

如果Optionala为空,NoSuchElementException将抛出a。如果Optional填充,则将返回包装器值。该orElseThrow方法,因此,等同于get在其功能的方法,但它的名字更好地描述它的目的是什么。因此,该orElseThrow方法应在不填充NoSuchElementExceptionan的情况下Optional有可能抛出值的任何地方使用,而无需首先检查它是否已填充(即,不使用isPresentorisEmpty方法或查询方法)。

get仅当在一种Optional查询方法中使用该方法时,才应保留该方法以供使用(即,Optional首先检查的填充状态或空状态)。请注意,此orElseThrow方法是JDK 9中引入的,它是一种减少使用该get方法引起的混乱的方法,应优先于get方法使用。

根据Brian Goetz的说法:

...我们[在Java 8中]犯的几个错误之一是的命名Optional.get(),因为该名称只是邀请人们调用而无需调用isPresent()Optional这首先损害了整个使用的重点....

在Java 9的时间范围内,我们建议弃用Optional.get(),但是公众对此表示反对。。。说得很冷。作为较小的步骤,我们orElseThrow()在[Java] 10 ...中引入了它,作为的当前有害行为的更透明的同义词get()。

所述Optional类还包括过载的orElseThrow抛出定制异常,当所述方法Optional是空的。此方法接受Suppler创建任何Throwable对象(或作为其子类的对象)Throwable并将其抛出的对象。例如:

Optional<Foo> possibleFoo = doSomething();

Foo foo = possibleFoo
    .orElseThrow(() -> { /* ...lazily create a Foo object... */ });

当客户端将丢失的对象视为错误并希望在访问Empty时引发异常时,此功能很有用Optional。使用构造函数的功能形式抛出简单异常也是一种常见的做法:

Optional<Foo> possibleFoo = doSomething();

Foo foo = possibleFoo
    .orElseThrow(SomeException::new);

ifPresent Family 如果填充ifPresentConsumer,则该方法接受使用包装的值执行操作Optional的。这是使用orElseororElseThrow方法获得包装对象的一种功能替代方法,主要是当我们不希望在不存在该值时不执行任何操作时。例如:

Optional<Foo> possibleFoo = doSomething();
possibleFoo.ifPresent(foo -> { /* ...do something with foo... */ });

Optional类还包括了类似的方法,ifPresentOrElse即允许我们处理的情况下,当Optional是空的为好。该ifPresentOrElse方法接受的第一个参数是a Consumer,如果Optional填充了,则使用包装的值执行操作,而第二个参数是a Runnable,如果填充Optional为空,则执行操作。因此,Consumer仅当Optional填充了Runnable时才调用,而仅当填充Optional为空时才调用。例如:

Optional<Foo> possibleFoo = doSomething();

possibleFoo.ifPresentOrElse(
    foo -> { /* ...do something with foo... */ },
    () -> { /* ...do something when no foo found... */ }
);

这两种方法的好处是,Consumer如果的Optional值为空,则永远不会调用。同样,对于ifPresentOrElseRunnable如果Optional填充了,则永远不会调用。这允许我们提供复杂或昂贵的操作,根据操作状态会延迟调用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 该map方法允许我们将包装的值从一个对象转换为另一个(如果Optional已填充)。可以将此方法视为管道方法,其中包装的值沿管道传递并转换为新值。该方法通过接受Function应用于包装值以生成映射值的对象来工作。如果Optionals为空,Function则从不调用该对象,并且Optional从该map方法返回一个空值。

当我们不知道是否存在值时,此方法很有用,但如果存在,则应将其转换为另一个对象。这是从数据库中读取数据时的一种常见用例,该数据库通常存储数据传输对象(DTO)。在大多数应用程序中,DTO用于将域对象有效地存储在数据库中,但是在应用程序的更高级别,域对象本身是必需的。因此,我们必须从DTO转换为域对象。

如果执行数据库对象的查找,则可能会或可能不会找到该对象。因此,这是返回Optional包装DTO的好用例。为了转换为域对象,我们可以使用该map方法。例如,假设我们有一个DTO(PersonDto),它将Person对象的名称存储在一行中,而该Person对象的名称则分为名字和姓氏(即,该名称"John Doe"与PersonDto对象中的名称相同,但是与对象一起存储)的名字"John"和姓氏"Joe"的Person对象)。我们可以使用一个映射器对象从转换PersonDtoPerson对象,并使用该映射器将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方法应返回一个empty Optional。执行此技术的一种反模式是使Function对象return null,然后将其通过map方法包装(使用)ofNullable,该方法允许使用我们的null对象进行包装而不会引发异常,将其包装为空Optional:

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方法并显式返回一个empty Optional

flatMapflatMap方法与方法类似map,不同之处在于flatMapaccept接受一个Function将包装后的值转换为new的对象Optional。与map方法不同,它flatMap允许我们返回Optional选择的内容。因此,Optional如果映射Function无法转换包装的值,我们可以显式返回一个空:

Optional<Person> person = db.findPerson()
    .flatMap(dto -> {
        if (dtoCanBeConverted()) {
            Person person = return dao.fromDto(dto);
            return Optional.ofNullable(person);
        }
        else {
            return Optional.empty();
        }
    });

重要的是要注意,我们不再能够Person像使用该map方法那样简单地返回一个对象。相反,我们现在负责将转换后的对象包装到中Optional。请注意,如果Function对象返回a null Optional,则会引发NPE。例如,以下将在执行时抛出NPE:

Optional<Person> person = db.findPerson()
    .flatMap(dto -> null);

filter 如果填充满足提供filter的,Optional则该方法允许我们返回填充OptionalPredicate。因此,如果将该filter方法应用于empty OptionalPredicate则不会调用。同样,如果将filter方法应用于已填充Optional,但包装的值不满足提供的值Predicate(即,对象的test方法Predicate求值为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

When & When Not to Use Optional该类最有争议的方面之一是何时应该使用和不应该使用它。在本节中,我们将介绍一些常见的用例,例如方法返回值,字段和参数,其中Optionals可能适用或可能不太适用。

Return Values 正如我们在本文中所看到的那样,Optionals非常适合方法返回值,因为这是其预期目的。根据Optional课程文件:

Optional主要用于需要明确表示“无结果”并且使用null可能会导致错误的方法返回类型。

通常,Optional如果满足以下条件,则应将an用作返回值:

  1. 预期可能存在或可能不存在值
  2. 如果缺少值,这不是错误
  3. 客户负责处理价值缺失的情况 Optional返回值通常用于可能无法找到所需对象的查询。例如,存储库通常将以以下方式定义:
public interface BookRepository {
    public Optional<Book> findById(long id);
    public Optional<Book> findByTitle(String title);
    // ...
}

这允许客户端以Book适合于调用方法的上下文的方式来处理丢失的对象(例如,通过忽略丢失的对象,创建默认对象或引发异常)。

Fields 尽管Optional对象非常适合返回类型,但它们不太适合实例字段。可以创建类似于以下内容的字段,但是强烈建议:

public class Bar {

    private Optional<Foo> foo;

    // ...getters & setters...
}

Optional应避免使用字段,因为Optional该类不可序列化(即,未实现该Serializable接口)。根据Brian Goetz的说法:

当然,人们会做他们想做的事。但是,添加此功能时我们确实有明确的意图,并且它并不是通用的Maybe类型,因为许多人希望我们这样做。我们的意图是为库方法返回类型提供一种有限的机制,该机制需要一种明确的方法来表示“无结果”,并且使用null这种方法极有可能导致错误。

因此,Optional类型仅用于方法返回类型。由于字段构成类的内部状态,并且不应对外部客户端可见,因此,如果将字段视为可选字段,则可以创建一个getter来返回一个Optional对象:

public class Bar {

    private Foo foo;

    public Optional<Foo> getFoo() {
        return Optional.ofNullable(foo);
    }
}

使用此技术,可以明确告知客户端该foo值可能存在或可能不存在,同时Bar保持的可序列化性。

Parameters 在某些情况下,方法或构造函数的参数可能是可选的,但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) {
        // ...
    }
}

Alternatives 尽管Optional该类在正确的上下文中有效,但是当找不到或可能找不到所需的值时,它不是唯一可以使用的方法。在本节中,我们介绍了Optional该类的三种替代方法,以及如何在适当的上下文中应用它们。

null 如null本文开头所见,最简单的替代方法是使用use 。尽管此方法确实实现了我们的目标,但在引入Optional类之后,null仅当Optional对象需要太多开销时才应使用s 。这种开销可以是Optional包装类所需的额外内存,也可以是执行Optional方法所需的额外周期。

将性能作为使用nulls的借口可能很诱人,而Optionals会更有效,但是在大多数应用程序中,Optional该类增加了微不足道的开销。除非我们使用低级代码,就像来自网络或驱动程序的字节一样,或者我们使用的是非常大量的数据,Optional否则null对于方法返回类型,始终应首选s而不是s。

Null Object 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();

重要的是要注意,空对象的引入假定该方法本身知道如何处理缺少值的情况。

Exceptions 我们在本文中看到的另一种替代方法是在找不到所需对象时引发异常。如果该方法知道找不到所需对象是错误,则此方法有效。例如:

public class ArticleRepository {
    public Article findById(long id) {

        if (articleIsFound()) {
            // return article...
        }
        else {
            throw new ArticleNotFoundException();
        }
    }
}

结论 在许多情况下,应用程序中可能存在或可能不存在所需的值,并且以可读有效的方式处理这些情况是精心设计的软件的重要组成部分。从JDK 7开始,Java包含了Optional该类,该类允许开发人员返回可能存在或可能不存在的值,并允许客户端根据它们发生的上下文来处理这些情况。尽管Optional该类仅应用于方法返回值,但了解其用途以及如何使用简单的技术来应用它是掌握现代Java的重要组成部分。


原文链接:http://codingdict.com