Java8 Optional用法和最佳实践

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

在Optional之前

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

// Life before Optional
    private void getIsoCode( User user){
        if (user != null) {
            Address address = user.getAddress();
            if (address != null) {
                Country country = address.getCountry();
                if (country != null) {
                    String isocode = country.getIsocode();
                    if (isocode != null) {
                        isocode = isocode.toUpperCase();
                    }
                }
            }
        }
    }

为了简化此过程,让我们看一下如何使用Optional类,从创建和验证实例到使用它提供的不同方法并将其与返回相同类型的其他方法组合在一起,后者才是Optional的厉害之处。

Optional的特性

Optional类提供了大约10种方法,我们可以使用它们来创建和使用Optional类,下面将介绍如何使用它们。

创建一个Optional类

这是用于创建可选实例的三种创建方法。

  1. static <T> [Optional]<T> [empty]()

返回一个空的Optional实例。

// Creating an empty optional
Optional<String> empty = Optional.empty();

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

  1. static <T> [Optional]<T> [of](T value)

返回特定的非空值Optional。

// Creating an optional using of

String name = "java";

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

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

  1. static <T> [Optional]<T> [of](T value)

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

// Possible null value

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

  private  String  name(){

  String name = "Java";

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

 }

如果我们传入一个空引用,它不会抛出异常,而是返回一个空的Optional对象:

所以这就是动态或手动创建Optional的三种方法。下一组方法用于检查值的存在。

  1. 布尔值[isPresent]()

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

Optional<String> optional1 = Optional.of("javaone");

if (optional1.isPresent()){

//Do something, normally a get

}
···

  1. 布尔值[isEmpty()]

如果存在值,则返回false;否则,返回ture。这与isPresent 相反, 并且仅在Java 11及更高版本中可用。

 //isempty

Optional<String> optional1 = Optional.of("javaone");

if (optional1.isEmpty()){

  //Do something

}
  1. void [ifPresent]([Consumer]<? super [T]> consumer)

如果存在值,则使用该值调用指定的使用者;否则,什么都不做。

如果您不熟悉Java 8,那么您可能会想知道:什么是消费者?简单来说,消费者是一种接受参数且不返回任何内容的方法。当使用 ifPresent时,这个方法就是一石二鸟。我们可以执行值存在性检查并使用一种方法执行预期的操作,如下所示。

//ifpresent

Optional<String> optional1 = Optional.of("javaone");

optional1.ifPresent(s -> System.out.println(s.length()));

可选类提供了另一组用于获取可选值的方法。

  1. T[get]()

如果此Optional中存在值,则返回该值,否则抛出 NoSuchElementException。在这之后,我们想要的是存储在Optional中的值,我们可以通过get()来获取它。但是,当该值为null时,此方法将引发异常。这就需要 orElse() 方法来紧急救援。

//get
Optional<String> optional1 = Optional.of("javaone");
if (optional1.isPresent()){ 
  String value = optional1.get();
}
  1. [T ][orElse][T]其他)

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

该 orElse() 方法用于检索包装在Optional实例内的值。它采用一个充当默认值的参数。该 orElse() 方法返回包装的值(如果存在)及其参数,反之:

 //orElse
        String nullName = null;
        String name = Optional.ofNullable(nullName).orElse("default_name");

如果这还不够,那么Optional类将继续提供另一种获取值的方法,即使该方法的null称为 orElseGet()。

  1. [T][orElseGet]([Supplier]<? extends [T]> other)

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

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

  //orElseGet
        String name = Optional.ofNullable(nullName).orElseGet(() -> "john");

那么,orElse() 和orElseGet()之间有什么区别。

乍一看,这两种方法似乎具有相同的效果。但是,事实并非如此。让我们创建一些示例,以突出两者之间的相似性和行为差异。

首先,让我们看看它们在对象为空时的行为:

String text = null;
String defaultText = Optional.ofNullable(text).orElseGet(this::getDefaultValue);
defaultText = Optional.ofNullable(text).orElse(getDefaultValue());
public String getDefaultValue() {
    System.out.println("Getting Default Value");
    return "Default Value";
}

在上面的示例中,我们在Optional对象中包装了一个空文本,然后尝试使用两种方法中的每一种来获取包装后的值。副作用如下:

Getting default value...
Getting default value...

在每种情况下都会调用默认方法。碰巧的是,当不存在包装的值时,两者 orElse() 和的 orElseGet() 工作方式完全相同。

现在,让我们运行另一个该值存在测试,理想情况下,甚至不应创建默认值:

在这个简单的示例中,创建默认对象不会花费很多成本,因为JVM知道如何处理此类对象。但是,当诸如此类的方法 default 必须进行Web服务调用或者查询数据库时,则成本变得非常明显。

使用Optional最佳实践

就像编程语言的任何其他功能一样,它可以正确使用或被滥用。为了了解使用Optional类的最佳方法,需要了解以下内容:

1.它解决的问题

Optional的方法是尝试通过增加构建更具表现力的API的可能性来减少Java系统中空指针异常的情况,这些API解释了有时缺少返回值的可能性。

如果从一开始就存在Optional,那么大多数库和应用程序可能会更好地处理缺少的返回值,从而减少了空指针异常的数量以及总体上的错误总数。

2.它不解决的问题

Optional并不意味着是一种避免所有类型的空指针的机制。例如,它仍然必须测试方法和构造函数的强制输入参数。

像使用null时一样,Optional不能帮助传达缺失值的含义。以类似的方式,null可能意味着很多不同的东西(找不到值等),因此缺少Optional值也可以。

该方法的调用方仍然需要检查该方法的JavaDoc以理解缺省选项的含义,以便正确地处理它。

同样,以一种类似的方式,可以将检查的异常捕获在一个空块中,没有什么阻止调用方进行调用 get() 并继续进行。

3.何时使用

Optional的预期用途主要是作为返回类型。获取此类型的实例后,可以提取该值(如果存在)或提供其他行为(如果不存在)。

Optional类的一个非常有用的用例是将其与流或返回Optional值以构建流畅的API的其他方法结合。请参见下面的代码段

User user = users.stream().findFirst().orElse(new User("default", "1234"));

4.什么时候不使用

a)不要将其用作类中的字段,因为它不可序列化

如果确实需要序列化包含Optional值的对象,则Jackson库提供了将Optionals视为普通对象的支持。这意味着Jackson将空对象视为空,将具有值的对象视为包含该值的字段。可以在jackson-modules-java8项目中找到此功能。

b)不要将其用作构造函数和方法的参数,因为这会导致不必要的复杂代码。

User user = new User("john@gmail.com", "1234", Optional.empty());

本人创业团队产品MadPecker,主要做BUG管理、测试管理、应用分发
网址:[www.madpecker.com],有需要的朋友欢迎试用、体验!
本文为MadPecker团队技术人员译制,转载请标明出处

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容