学会在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。因此,我们必须调用orElseorElseThrow等方法——或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:如果Optionalempty,则返回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需要我们首先使用isPresentisEmpty方法查询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方法,并使用其他方法之一(如orElseorElseThrow)来获取与填充的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,而无需首先检查它是否已填充(即不使用isPresentisEmpty查询方法)。

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

如果填充了Optionalmap方法允许我们将包装值从一个对象转换为另一个对象。这种方法可以被认为是一种管道方法,其中包装的值沿着管道传递并转换为新值。此方法的工作原理是接受应用于包装值的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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java中Optional的使用指南

    提到NullPointerException(简称NPE)异常,相信每个Java开发人员都不陌生,从接触编程的第1天起,它就和我们如影随形,最近处理的线上bug中,有不少都是对象没判空导致的NullPointerException异常. 1. 简单回顾 引起NullPointerException异常的地方有很多,比如调用String的trim()方法,比如对BigDecimal进行计算时,比如将包装类型转化为基本类型时,这里简单回顾下. 假设有个导入模版定义如下: package com.zw

  • Java如何使用Optional与Stream取代if判空逻辑(JDK8以上)

    通过本文你可以用非常简短的代码替代业务逻辑中的判null校验,并且很容易的在出现空指针的时候进行打日志或其他操作. 注:如果对Java8新特性中的lambda表达式与Stream不熟悉的可以去补一下基础,了解概念. 首先下面代码中的List放入了很多Person对象,其中有的对象是null的,如果不加校验调用Person的getXXX()方法肯定会报空指针错误,一般我们采取的方案就是加上if判断: public class DemoUtils { public static void main(

  • java中Optional的使用详细解析

    Optional的使用详解 1.Optional介绍 Optional 类是一个可以为null的容器对象.如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象. Optional 是个容器:它可以保存类型T的值,或者仅仅保存null.Optional提供很多有用的方法,这样我们就不用显式进行空值检测. Optional 类的引入很好的解决空指针异常. 2.构建Optional 构建一个Optional对象:方法有:empty( ).of( ).ofNullable

  • Java8中Optional类的使用说明

    目录 简介 历史 null带来的种种问题 方案 场景引入 方法说明 构造函数 创建Optional对象 使用map从Optional对象中提取和转换值 使用flatMap链接Optional对象 默认行为及解引用Optional对象1 默认行为及解引用Optional对象2 使用filter剔除特定的值 实战 总结 简介 optional类是java8中引入的针对NPE问题的一种优美处理方式,源码作者也希望以此替代null. 历史 1965年,英国一位名为Tony Hoare的计算机科学家在设计

  • JAVA开发常用类库UUID、Optional、ThreadLocal、TimerTask、Base64使用方法与实例详解

    1.UUID类库 UUID 根据时间戳实现自动无重复字符串定义 // 获取UUID public static UUID randomUUID() // 根据字符串获取UUID public static UUID fromString(String name) 应用:对文件进行自动命名处理 import java.util.UUID; class Demo { public static void main(String[] args) { System.out.println(UUID.ra

  • 如何使用Java中的Optional

    NullPointerException是非常常见的异常.由于它,程序往往需要大量使用if-else代码块来处理空值,这使得代码看起来不简洁 优雅 ,且不方便自己和他人阅读.本文介绍如何用Optional类来处理null值问题. Optional类 先来看一段代码: String isocode = user.getAddress().getCountry().getIsocode().toUpperCase(); 这段代码在任何一个方法调用时,都有可能抛出NullPointerExceptio

  • 详解Java中Optional类的使用方法

    目录 一.Optional类的来源 二.Optional类是什么 三.Optional类用法 四.代码示例 1.创建Optional类 2.判断Optional容器中是否包含对象 3.获取Optional容器的对象 4.过滤 5.映射 五.什么场景用Optional 1.场景一 2.场景二 3.场景三 4.场景四 一.Optional类的来源 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因.以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optiona

  • Java8 Optional的详细使用教程

    Optional介绍 Optional是Jdk1.8提供的一个新类,希望可以通过该类的引入解决令人烦躁的null判断问题,非常好用.个人理解:这个类是一个包装类,将要操作的java bean封装到该类的对象里面,同时将一些常用的判断逻辑封装为成员方法,结合lambda语法,实现比较优雅的链式调用.现在对Optional的API使用做一个简单的说明. API介绍 Optional的所有的方法如下图所示,这些API大致可以分为以下几类: 1.构建API:构建一个Optional对象:方法有:empt

  • Java Optional解决空指针异常总结(java 8 功能)

    1.概述 Java8的版本,新增了Optional和[Lambda]表达式,Optional主要用于作为返回类型(主要解决的问题是臭名昭著的空指针异常 (NullPointerException)),并将其与流(或返回可选的方法)相结合以构建连贯API. 但是,有些情况可以被认为是陷阱,因为它们会降低代码的质量,甚至导致意想不到的错误.总结以下26个例子,以避免这些陷阱. 2. 目 录 [第1项:决不将Null分配给可选变量] [第2项:调用Optional.get()之前,确保Optional

  • 学会在Java中使用Optional功能

    目录 前言 Nullity Optional Class 客户责任 null Optional Objects 重要方法 创建方法 of ofNullable empty 实例方法 isPresent&isEmpty get orElse系列 orElseThrow系列 ifPresent系列 map flatMap filter 何时使用 返回值 字段 参数 替代方案 null 空对象 例外情况 结论 前言 尽管存在争议,但Optiont极大地改进了Java应用程序的设计.在本文中,我们将了解

  • 详解JAVA中的OPTIONAL

    一.概述 本质上,这是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空. Optional 是 Java 实现函数式编程的强劲一步,并且帮助在范式中实现.但是 Optional 的意义显然不止于此. 我们从一个简单的用例开始.在 Java 8 之前,任何访问对象方法或属性的调用都可能导致NullPointerException: String isocode = user.getAddress().getCountry().getIsocode().toUpper

  • JS自定义对象实现Java中Map对象功能的方法

    本文实例讲述了JS自定义对象实现Java中Map对象功能的方法.分享给大家供大家参考.具体分析如下: Java中有集合,Map等对象存储工具类,这些对象使用简易,但是在JavaScript中,你只能使用Array对象. 这里我创建一个自定义对象,这个对象内包含一个数组来存储数据,数据对象是一个Key,可以实际存储的内容!   这里Key,你要使用String类型,和Java一样,你可以进行一些增加,删除,修改,获得的操作. 使用很简单,我先把工具类给大家看下: 复制代码 代码如下: /**  *

  • Java中的ThreadLocal功能演示示例

    除了使用synchronized同步符号外,Java中的ThreadLocal是另一种实现线程安全的方法.在进行性能测试用例的编写过程中,比较简单的办法就是直接使用synchronized关键字,修饰对象.方法以及类.但是使用synchronized同步,这可能会影响应用程序的可伸缩性以及运行效率.但是如果要在多个线程之间共享对象又要保障线程安全,则除了synchronized之外没有特别适合测试的方法. Java中的ThreadLocal是实现线程安全的另一种方法,它不满足同步要求,而是通过为

  • Java中的Optional处理方法

    目录 java.util.Optional 使用Optional构建对象 获取Optional中的对象 Optional 中map和flatmap的差别 在我们日常的开发中,我们经常会遇到 NullPointerException.如何才能优雅的处理NPE?这里告诉大家一个较为流行的方法 java.util.Optional 使用Optional来修饰对象,表示这个对象可能为null.在使用时,就要加以注意,必须要考虑该值为null的场景. 使用Optional构建对象 // 创建一个空的car

  • Java中Optional类及orElse方法详解

    目录 引言 Java 中的 Optional 类 ofNullable() 方法 orElse() 方法 案例 orElseGet() 方法 案例 orElse() 与 orElseGet() 之间的区别 引言 为了让我更快的熟悉代码,前段时间组长交代了一个小任务,大致就是让我整理一下某个模块中涉及的 sql,也是方便我有目的的看代码,也是以后方便他们查问题(因为这个模块,涉及的判断很多,所以之前如果 sql 出错了,查问题比较繁琐). 昨天算是基本完成了,然后今天组长就让给我看一个该模块的缺陷

  • java中optional的一些常用方法总结

    目录 前言 1. 创建Optional对象 2. 获取Optional对象的值 3. 判断Optional对象是否包含非空值 4. 获取Optional对象中的值或默认值 5. 获取Optional对象中的值或抛出异常 6. 转换Optional对象中的值 7. 过滤Optional对象中的值 Java 9 增强 补充:Optional争议点 总结 前言 Java中的Optional是一个容器对象,它可以包含一个非空值,也可以为空.它的主要作用是在编写代码时避免空指针异常. java 8 中Op

  • 详解Java中Iterator迭代器的用法

    迭代器(Iterator) 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构.迭代器通常被称为"轻量级"对象,因为创建它的代价小. Java中的Iterator功能比较简单,并且只能单向移动: (1) 使用方法iterator()要求容器返回一个Iterator.第一次调用Iterator的next()方法时,它返回序列的第一个元素.注意:iterator()方法是java.lang.Iterable接口,被Collection继承

随机推荐