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){Countrycountry=address.getCountry();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());}但是,你看到Arrived了,这个方法在值为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());}这里的用户对象是空的,因此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()之间的差异乍一看,这两个方法似乎做同样的事情。然而,事实并非如此。我们创建了一些示例来突出两者之间行为的异同。让我们看看它们在对象为空时的行为:@TestpublicvoidgivenEmptyValue_whenCompare_thenOk(){Useruser=nulllogger.debug("UsingorElse");Userresult=Optional.ofNullable(user).orElse(createNewUser());logger.debug("UsingorElseGet");Userresult2=Optional.ofNullable(user).orElseGet(()->createNewUser());}privateUsercreateNewUser(){logger.debug("CreatingNewUser");returnnewUser("extra@gmail.com","1234");}在上面的代码中,两个方法都调用了createNewUser()方法,该方法将记录一条消息并返回User对象。代码输出如下:UsingorElseCreatingNewUserUsingorElseGetCreatingNewUser可以看出,当对象为空和返回默认对象时,行为没有区别。让我们看一个类似的例子,但是这里Optional不为空:@TestpublicvoidgivenPresentValue_whenCompare_thenOk(){Useruser=newUser("john@gmail.com","1234");logger.info("UsingorElse");Userresult=Optional.ofNullable(user).orElse(createNewUser());logger.info("UsingorElseGet");Userresult2=Optional.ofNullable(user).orElseGet(()->createNewUser());}这时候输出:UsingorElseCreatingNewUserUsingorElseGet这个在例如,两个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()应用(调用)一个函数作为一个值的参数,然后包装返回的可选中间值。这使得对返回值进行链式测试调用成为可能——这里的下一个循环是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(user).flatMap(u->u.getPosition()).orElse("default");assertEquals(position,user.getPosition().get());}过滤值除了转换值,Optional类还提供了Method用于值的条件“过滤”。filter()采用Predicate参数并返回测试评估为true的值。如果测试评估为false,则返回一个空的Optional。让我们看一个基于基本电子邮件验证接受或拒绝用户的示例:@TestpublicvoidwhenFilter_thenOk(){Useruser=newUser("anna@gmail.com","1234");Optionalresult=Optional.ofNullable(user).filter(u->u.getEmail()!=null&&u.getEmail().contains("@"));assertTrue(result.isPresent());}如果过滤测试通过,结果对象将包含非-空值。