前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java8 Optional用法和最佳实践

Java8 Optional用法和最佳实践

原创
作者头像
一觉睡到小时候
发布2022-11-22 14:33:23
1K0
发布2022-11-22 14:33:23
举报
文章被收录于专栏:国产程序员

根据Oracle文档,Optional是一个容器对象,可以包含也可以不包含非null值。Optional在Java 8中引入,目的是解决  NullPointerExceptions的问题。本质上,Optional是一个包装器类,其中包含对其他对象的引用。在这种情况下,对象只是指向内存位置的指针,并且也可以指向任何内容。从其它角度看,Optional提供一种类型级解决方案来表示可选值而不是空引用。

## 在Optional之前

在Java 8之前,程序员将返回null而不是Optional。这种方法有一些缺点。一种是没有明确的方法来表示null可能是一个特殊值。如果我们要确保不会出现空指针异常,则需要对每个引用进行显式的空检查。

另外还有一些开发人员喜欢通过非空检查来实现业务逻辑,空对象不应该用来决定系统的行为,它们是意外的Exceptional值,应当被看成是错误,而不是业务逻辑状态。

当我们一个方法返回List集合时,应该总是返回一个空的List,而不是Null,这就允许调用者能够遍历它而不必检查Null,否则就抛出NPE。

```

private void getCode( User user){

if (user != null) {

Address address = user.getAddress();

if (address != null) {

Country country = address.getCountry();

if (country != null) {

String code = country.getCode();

if (code != null) {

code = code.toUpperCase();

}

}

}

}

}

```

## Optional

### 构造Optional的三种方式

```

public static<T> Optional<T> empty() {

Optional<T> t = (Optional<T>) EMPTY;

return t;

}

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);

}

```

### 创建一个Optional类

#### 返回一个空的Optional实例

```

// Creating an empty optional

Optional<String> empty = Optional.empty();

```

在返回一个空的{Optional}实例时,Optional的值不存在。不过,这样做可能很有诱惑力,如果对象为空,请避免与Option.empty()返回的实例的{==}比较 。因为不能保证它是一个单例,反之,应该使用isPresent()。

#### 返回特定的非空值Optional

```

// Creating an optional using of

String name = "java";

Optional<String> opt = Optional.of(name);

```

静态方法需要一个非null参数;否则,将引发空指针异常。因此,如果我们不知道参数是否为null,那就是我们使用 ofNullable的时候,下面将对此进行介绍。

#### 返回描述指定值的Optional,如果非空,则返回空值

```

// Possible null value

Optional<String> optional = Optional.ofNullable(name());

private String name(){

String name = "Java";

return (name.length() > 5) ? name : null;

}

```

### 常用API

#### ifPresent()

如果存在值,则返回true;反之,返回false。如果所包含的对象不为null,则返回true,反之返回false。通常在对对象执行任何其他操作之前,先在Optional上调用此方法。

```

Stream<String> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

Optional<String> longest = names

.filter(name -> name.startsWith("L"))

.findFirst();

longest.ifPresent(name -> {

String s = name.toUpperCase();

System.out.println("The longest name is "+ s);

});

//The longest name is LAMURUDU

```

这里ifPresent() 是将一个Lambda表达式作为输入,T值如果不为空将传入这个lambda。那么这个lambda将不为空的单词转为大写输出显示。在前面names单词流寻找结果中,有可能找不到开始字母为L的单词,返回为空,也可能找到不为空,这两种情况都传入lambda中,无需我们打开盒子自己编写代码来判断,它自动帮助我们完成了,无需人工干预。

#### map()

使用Optional<T>的map方法能够返回另外一个Optional

```

Stream<String> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

Optional<String> longest = names

.filter(name -> name.startsWith("L"))

.findFirst();

Optional<String> lNameInCaps = longest.map(String::toUpperCase);

lNameInCaps.ifPresent(name -> {

System.out.println("The name is "+ name);

});

//The name is LAMURUDU

```

#### orElse()

返回值(如果存在);反之,返回其他。

```

Stream<String> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

Optional<String> longest = names

.filter(name -> name.startsWith("Q"))

.findFirst();

String alternate = longest.orElse("Nimrod");

System.out.println(alternate);

//Nimrod

```

#### orElseGet()

返回值(如果存在);否则,调用other并返回该调用的结果。

该orElseGet() 方法类似于 orElse()。但是,如果没有Optional值,则不采用返回值,而是采用供应商功能接口,该接口将被调用并返回调用的值。

```

Stream<String> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

Optional<String> longest = names

.filter(name -> name.startsWith("Q"))

.findFirst();

String alternate = longest.orElseGet(() -> {

return "Nimrod";

});

System.out.println(alternate);

//Nimrod

```

#### orElseThrow()

orElseThrow()是在当遭遇Null时,决定抛出哪个Exception时使用

```

Stream<String> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

Optional<String> longest = names

.filter(name -> name.startsWith("Q"))

.findFirst();

longest.orElseThrow(NoSuchElementStartingWithQException::new);

```

### orElse() 和orElseGet()之间有什么区别

我们可能考虑的问题是:何时使用orElse和何时使用orElseGet?看起来可以使用orElseGet的时候,使用orElse也可以代替(因为Supplier接口没有入参),而且使用orElseGet还需要将计算过程额外包装成一个 lambda 表达式。

一个关键的点是,使用Supplier能够做到懒计算,即使用orElseGet时。它的好处是,只有在需要的时候才会计算结果。具体到我们的场景,使用orElse的时候,每次它都会执行计算结果的过程,而对于orElseGet,只有Optional中的值为空时,它才会计算备选结果。这样做的好处是可以避免提前计算结果的风险。

```

class User {

// 中文名

private String chineseName;

// 英文名

private EnglishName englishName;

}

class EnglishName {

// 全名

private String fullName;

// 简写

private String shortName;

}

```

假如我们现在有User类,用户注册账号时,需要提供自己的中文名或英文名,或都提供,我们抽象出一个EnglishName类,它包含英文名的全名和简写(因为有的英文名确实太长了)。现在,我们希望有一个User#getName()方法,它可以像下面这样实现:

```

class User {

// ... 之前的内容

public String getName1() {

return Optional.ofNullable(chineseName)

.orElse(englishName.getShortName());

}

public String getName2() {

return Optional.ofNullable(chineseName)

.orElseGet(() -> englishName.getShortName());

}

}

```

两个版本,分别使用orElse和orElseGet。现在可以看出getName1()方法有什么风险了吗?它会出现空指针异常吗?

答案是:是的。当用户只提供了中文名时,此时englishName属性是null,但是在orElse中,englishName.getShortName()总是会执行。而在getName2()中,这个风险却没有。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档