Java8引入的一个很有意思的特性就是Optional类。Optional类解决的主要问题是臭名昭著的NullPointerException——每个Java程序员都非常了解的异常。本质上,这是一个围绕可选值的包装类,这意味着Optional类可以包含一个对象或为空。Optionals是朝着Java中的函数式编程迈出的重要一步,有助于在范例中实现它们。但Optional的意义显然不止于此。我们从一个简单的用例开始。在Java8之前,任何访问对象方法或属性的调用都可能导致NullPointerException:Stringisocode=user.getAddress().getCountry().getIsocode().toUpperCase();要触发异常,您必须在访问每个值之前明确检查它:if(user!=null){Addressaddress=user.getAddress();if(address!=null){if(country!=null){Stringisocode=country.getIsocode();if(isocode!=null){isocode=isocode.toUpperCase();}}}}你看,这很容易变得冗长,难以维护。为了简化这个过程,让我们看看它是如何用Optional类完成的。从创建和验证实例,到使用它的不同方法,再到将它们与返回相同类型的其他方法组合,这些时刻见证了Optional的魔力。创建Optional的实例同样,此类型的对象可能包含值,也可能为空。您可以使用同名方法创建一个空的Optional。@Test(expected=NoSuchElementException.class)publicvoidwhenCreateEmptyOptional_thenNull(){OptionalemptyOpt=Optional.empty();emptyOpt.get();}不出所料,尝试访问emptyOpt变量的值会导致NoSuchElementException。您可以使用of()和ofNullable()方法创建一个包含值的Optional。这两种方法的区别在于,如果你传递一个空值作为参数,of()方法将抛出一个NullPointerException:.of(user);}你看,我们并没有完全摆脱NullPointerException。因此,当对象不为null时,您应该显式使用of()。如果对象可能为null或非null,则应使用ofNullable()方法:Optionalopt=Optional.ofNullable(user);访问Optional对象的值从Optional实例中检索实际值对象的方法之一是使用get()方法:@TestpublicvoidwhenCreateOfNullableOptional_thenOk(){Stringname="John";Optionalopt=Optional.ofNullable(name);assertEquals("John",opt.get());}但是,如您所见,此方法在值为null时抛出异常。为避免异常,您可以选择先验证是否有值:@TestpublicvoidwhenCheckIfPresent_thenOk(){Useruser=newUser("john@gmail.com","1234");Optionalopt=Optional.ofNullable(user);assertTrue(opt.isPresent());assertEquals(user.getEmail(),opt.get().getEmail());}检查是否有值的另一个选项是ifPresent()方法。除了执行检查之外,该方法还接受一个Consumer(消费者)参数。如果对象不为空,执行传入的Lambda表达式:opt.ifPresent(u->assertEquals(user.getEmail(),u.getEmail()));在此示例中,仅当用户不为空时才会执行断言。接下来,让我们看一下提供空值的方法。返回默认值Optional类提供了一个API来返回对象值,或者当对象为空时返回默认值。您可以在此处使用的第一个方法是orElse(),它非常直接,如果存在则返回值,否则返回传递给它的参数的值:@TestpublicvoidwhenEmptyValue_thenReturnDefault(){Useruser=null;Useruser2=newUser("anna@gmail.com","1234");Userresult=Optional.ofNullable(user).orElse(user2);assertEquals(user2.getEmail(),result.getEmail());}这里user对象为空,所以返回user2作为默认值。如果对象的初始值不为空,默认值将被忽略:@TestpublicvoidwhenValueNotNull_thenIgnoreDefault(){Useruser=newUser("john@gmail.com","1234");Useruser2=newUser("anna@gmail.com","1234");Userresult=Optional.ofNullable(user).orElse(user2);assertEquals("john@gmail.com",result.getEmail());}的第二个API相同的类型是orElseGet()-其行为略有不同。当有值时,此方法将返回一个值。如果没有值,则执行作为参数传入的Supplier(供应商)功能接口,并返回其执行结果:Userresult=Optional.ofNullable(user).orElseGet(()->user2);orElse()和orElseGet()之间的差异乍一看,这两种方法似乎服务于相同的目的。然而,事实并非如此。我们创建了一些示例来突出两者之间行为的异同。让我们来看看它们在对象为空时的行为:logger.debug("UsingorElseGet");Userresult2=Optional.ofNullable(user).orElseGet(()->createNewUser());}privateUsercreateNewUser(){logger.debug("创建新用户");返回newUser("extra@gmail.com","1234");}在上面的代码中,两个方法都调用了createNewUser()方法,它会记录一条消息并返回User对象。代码输出如下:UsingorElseCreatingNewUserUsingorElseGetCreatingNewUser可以看出,当对象为空和返回默认对象时,行为没有区别。让我们看一个类似的例子,但这里Optional不是空的:Userresult=Optional.ofNullable(user).orElse(createNewUser());logger.info("UsingorElseGet");Userresult2=Optional.ofNullable(user).orElseGet(()->createNewUser());}此输出:使用orElseCreatingNewUserUsingorElseGet在此示例中,两个Optional对象都包含非空值,并且两个方法都返回相应的非空值。但是,orElse()方法仍然会创建一个User对象。相反,orElseGet()方法不创建用户对象。在执行更密集的调用(例如调用Web服务或查询数据)时,这种差异会对性能产生重大影响。返回异常除了orElse()和orElseGet()方法之外,Optional还定义了orElseThrow()API——当对象为空时它会抛出异常,而不是返回替代值:@Test(expected=IllegalArgumentException.class)publicvoidwhenThrowException_thenOk(){Userresult=Optional.ofNullable(user).orElseThrow(()->newIllegalArgumentException());}这里如果user的值为null,会抛出IllegalArgumentException。这个方法让我们有更丰富的语义,可以决定抛出什么样的异常,而不是总是抛出NullPointerException。现在我们已经很好地理解了如何使用Optionals,让我们看看其他可以转换和过滤Optional值的方法。转换值有多种方法可以转换Optional的值。我们从map()和flatMap()方法开始。我们先看一个使用map()API的例子:@TestpublicvoidwhenMap_thenOk(){Useruser=newUser("anna@gmail.com","1234");Stringemail=Optional.ofNullable(user).map(u->u.getEmail()).orElse("default@gmail.com");assertEquals(email,user.getEmail());}map()应用(调用)函数作为值的参数,然后将返回值包装在一个Optional中。这使得对返回值进行链式测试调用成为可能——这里的下一个循环是orElse()。相比之下,flatMap()也接受一个函数作为参数,对值调用函数,并直接返回结果。在下面的操作中,我们在User类中添加了一个返回Optional的方法:publicclassUser{privateStringposition;publicOptionalgetPosition(){returnOptional.ofNullable(position);}//...}因为getter方法返回一个带有String值的Optional,你可以在调用flatMap()用户的可选对象。返回值是展开的字符串值:@TestpublicvoidwhenFlatMap_thenOk(){Useruser=newUser("anna@gmail.com","1234");user.setPosition("Developer");Stringposition=Optional.ofNullable(用户).flatMap(u->u.getPosition()).orElse("default");assertEquals(position,user.getPosition().get());过滤值除了转换值,Optional类还提供了按条件“过滤”值的方法。filter()采用Predicate参数并返回测试评估为true的值。如果测试为假,则返回一个空的Optional。让我们看一个基于基本电子邮件验证接受或拒绝用户的示例:@TestpublicvoidwhenFilter_thenOk(){Useruser=newUser("anna@gmail.com","1234");Optionalresult=Optional.ofNullable(user).filter(u->u.getEmail()!=null&&u.getEmail().contains("@"));assertTrue(result.isPresent());}如果通过了过滤测试,则结果对象将包含非空值。Optional类的链接方法要充分利用Optional,您可以链接它的大部分方法,因为它们都返回相同的相似对象。让我们使用Optional重写第一个引入的示例。首先,重构该类,使其getter方法返回一个Optional引用:{私人国家国家;publicOptionalgetCountry(){returnOptional.ofNullable(country);}//...}上面的嵌套结构可以用下图来表示:TestpublicvoidwhenChaining_thenOk(){Useruser=newUser("anna@gmail.com","1234");Stringresult=Optional.ofNullable(user).flatMap(u->u.getAddress()).flatMap(a->a.getCountry()).map(c->c.getIsocode()).orElse("default");assertEquals(result,"default");}以上代码可以通过methodReferences进一步缩小:Stringresult=Optional.ofNullable(user).flatMap(User::getAddress).flatMap(Address::getCountry).map(Country::getIsocode).orElse("default");结果现在看代码比起之前使用条件分支的冗长代码,看起来简洁多了。Java9增强我们介绍了Java8功能,它向Optional类添加了三个方法:or()、ifPresentOrElse()和stream()。or()方法与orElse()和orElseGet()类似,因为它们都提供对象为null时的替代情况。or()的返回值是由Supplier参数生成的另一个Optional对象。如果对象包含一个值,Lambda表达式将不会执行:@TestpublicvoidwhenEmptyOptional_thenGetValueFromOr(){Userresult=Optional.ofNullable(user).or(()->Optional.of(newUser("default","1234"))).get();assertEquals(result.getEmail(),"default");}在上面的例子中,如果user变量为null,它将返回一个Optional,其中包含电子邮件为"默认”。ifPresentOrElse()方法有两个参数:Consumer和Runnable。如果对象包含值,则执行Consumer的操作,否则执行Runnable。如果您想在值存在时执行操作,或者只是跟踪值是否已定义,则此方法很有用:Optional.ofNullable(user).ifPresentOrElse(u->logger.info("Useris:"+u.getEmail()),()->logger.info("找不到用户"));最后,引入了新的stream()方法,它将实例转换为Stream对象,让您从API中获得巨大的Stream好处。如果没有值,它会得到一个空的Stream;如果有值,则Stream将包含单个值。让我们看一个将Optional处理成Stream的例子:@TestpublicvoidwhenGetStream_thenOk(){Useruser=newUser("john@gmail.com","1234");Listemails=Optional.ofNullable(user)。stream().filter(u->u.getEmail()!=null&&u.getEmail().contains("@")).map(u->u.getEmail()).collect(Collectors.toList());assertTrue(emails.size()==1);assertEquals(emails.get(0),user.getEmail());}这里使用Stream带来了它的filter(),map()和collect()获取列表的接口。边城边城翻译1天前由0人翻译顶顶不错的翻译!Optional应该如何使用?在使用Optional来决定何时以及如何使用它时,需要考虑一些事项。重要的一点是Optional不是可序列化的。因此,不应将其用作类的字段。如果您需要序列化的对象包含Optional值,Jackson库支持将Optional视为普通对象。也就是说,Jackson会把空对象当作null,有值的对象会把自己的值当作对应字段的值。此功能位于jackson-modules-java8项目中。边城边城翻译1天前由0人翻译顶顶不错的翻译!另一种不是很有用的情况是当它的类型用作方法或构造函数的参数时。这样做会使代码复杂化,完全没有必要:Useruser=newUser("john@gmail.com","1234",Optional.empty());使用重载方法更容易处理可选参数。Optional主要用作返回类型。得到这个类型的实例后,如果它有值,就可以获取值,否则可以进行一些替代行为。Optional类的一个非常有用的用例是将它与流或其他返回Optional的方法结合起来以构建流畅的API。我们来看一个例子,使用Stream的findFirst()方法返回Optional对象:().orElse(newUser("default","1234"));assertEquals(user.getEmail(),"default");}Optional是对Java语言的有用补充——它旨在减少代码中的NullPointerExceptions,尽管它不能完全消除这些异常。它也是一个精心设计的特性,很自然地适合Java8的功能支持。总的来说,这个简单但功能强大的类有助于创建比同类程序更简单、更易读且故障更少的程序。最后,flatMap和Map的区别是map会把获取到的值用Optional包装起来。例如,OptionalFlatMap将原封不动地返回它。如果类中的某个字段是Optionalname,那么在获取这个名字的时候需要使用Optional。.flatMap(Object::getName)获取https://blog.csdn.net/qq_3563...