相关文章推荐
逆袭的蘑菇  ·  【java框架】SpringMVC(4) ...·  2 周前    · 
淡定的地瓜  ·  iOS 通信通知 ...·  2 周前    · 
果断的饼干  ·  为 Microsoft HPC Pack ...·  3 月前    · 
叛逆的橡皮擦  ·  HTML 链接 | 菜鸟教程·  1 年前    · 
腼腆的柳树  ·  如何使用React创建QR ...·  1 年前    · 

Optional的本意是为了避免null带来的错误,为我们提供一个可以明确表示空值的有限的机制。

Optional是一个容器,用于放置可能为空的值,它可以合理而优雅的处理null。
Jdk1.8后,jdk新增了Optional来表示空结果。其实本质上什么也没有变,只是增加了一个表达式。
Optional表示空的静态方法为Optional.empty(),跟null有什么本质区别吗?看它的实现,Optional中的value就是null,只不过包了一层Optional,所以说它其实是个容器。用了之后的代码可能如下:

Optional < User > optionalUser = RemoteService . getUser ( ) ; if ( ! optionalUser . isPresent ( ) ) { //doing something User user = optionalUser . get ( ) ; User user = optionalUser . orElse ( new User ( ) ) ;

看起来,好像好了一点,至少看起来没那么笨了。但是如果采用写法1,好像更啰嗦了。

不合适的使用场景

直接使用isPresent()进行if检查

这个直接参考上面的例子,用if判断和1.8之前的写法并没有什么区别,反而返回值包了一层Optional,增加了代码的复杂性,没有带来任何实质性的收益。其实isPresent()一般用于流处理的结尾,用于判断是否符合条件。

boolean aaa = list.stream().filter(o -> Objects.equals(o, param)).findFirst().isPresent();

在方法参数中使用Optional

我们用一个东西之前得想明白,这东西是为解决什么问题而诞生的。
Optional直白点说就是为了表达可空性,如果方法参数可以为空,为何不重载呢?包括使用构造函数也一样。重载的业务表达更加清晰直观。

    public void getUser(String uuid, Optional<UserType> optional) {     
    public void getUser(String uuid) {
        getUser(uuid,null);
    public void getUser(String uuid, UserType userType) {
        // do something

直接使用Optional.get

Optional不会帮你做任何的空判断或者异常处理,如果直接在代码中使用Optional.get()和不做任何判断一样,十分危险。这可能会出现在那种所谓的急着上线,着急交付,对Optional也不是很熟悉,直接就用了。所以这就要我们平时保持学习,都是信手拈来的东西。

使用在注入的属性中

这种写法估计用的人很少,但是不排除脑洞大开的。

public class CommonService {
    private Optional<UserService> userService;
    public User getUser(String name) {
        return userService.ifPresent(u -> u.findByName(name));

首先依赖注入大多在spring的框架下,直接使用@Autowired很方便。但是如果使用以上的写法,如果userService设置失败了,程序就应该终止并报异常,并不是无声无息,让其看起来什么问题都没有。

Optional的API

empty()

返回一个Optional容器对象,而不是null。建议常用 ⭐⭐⭐⭐

// empty.get()值为null
Optional<Object> empty = Optional.empty();

of(T value)

创建一个Optional对象,如果value是null,则抛出NPE(null point exception)。不建议使用。

ofNullable(T value)

创建一个Optional对象,但是value为空时返回Optional.empty()。推荐使用⭐⭐⭐⭐⭐

get()

返回Optional中包装的值,在判空之前,千万不要直接使用!尽量别用。

orElse(T other)

同样返回Optional中包装的值,但不同的是当取不到值时,返回你指定的default。看似很好,但不建议使用⭐⭐

orElseGet(Supplier<? extends T> other)

同样是返回Optional中包装的值,取不到值是,返回你指定的default,看似和上面一样,但是推荐使用⭐⭐⭐⭐⭐

orElseThrow(Supplier<? extends X> exceptionSupplier)

返回Optional中包装的值,取不到时抛出指定的异常。阻塞性业务场景推荐使用⭐⭐⭐⭐

isPresent()

判断Optional中是否有值,返回boolean,某些情况下很好用,但尽量不用在if判断体中。可以用⭐⭐⭐

ifPresent(Consumer<? super T> consumer)

判断Optional中是否有值,有值则执行consumer,否则什么都不干。日常情况使用这样⭐⭐⭐⭐

一些基本原则:

  • 不要声明任何Optional实例属性。
  • 不要再任何setter或者构造方法中使用Optional
  • Optional属于返回类型,在业务返回值或者远程远程调用中使用

对象可空,需要获取对象属性

Optional.ofNullable(resData).orElse(Collections.emptyMap()).get("data")

业务上需要空值时,不要直接返回null,使用Optional.empty()

    public Optional<String> getOptionResult(String user) {
        if (Objects.nonNull(user)) {
            return Optional.ofNullable(user);
        return Optional.empty();

使用orElseGet()

使用orElseGet()主要是从性能的角度考虑。
获取value有三种方式:get(),orElse(),orElseGet()。这里推荐在需要用的地方只有orElseGet()。
首先,get()不能直接使用,需要结合判空使用。这和!=null其实没有多大区别,只是在表达和抽象上有所改善。
其次,为什么不推荐orElse()呢?因为orElse()不论如何都会指向括号中内容,orElseGet()只在主体value是null时才执行:

    public String getResult() {
        System.out.println("call result");
        return "result";
    public void testOrElseGet() {
        // out:
        Optional.ofNullable("result").orElseGet(() -> {
            System.out.println("orElseGet call exe");
            return "call";
        });
        // out: call result
        Optional.ofNullable("result").orElse(getResult());

如果上面的例子getResult()方法是一个远程调用,或者涉及到大量的文件IO,代价可想而知。
但orElse也是有用武之地的。orElseGet()需要构建一个Supplier函数接口对象,如果只是简单的返回一个静态资源、字符串等等,直接返回静态资源即可。

public static final String USER_STATUS = "UNKNOWN";
...
public String findUserStatus(long id) {
    Optional<String> status = ... ; // 
    return status.orElse(USER_STATUS);
//不要这么写
public String findUserStatus(long id) {
    Optional<String> status = ... ; // 
    return status.orElse("UNKNOWN");//这样每次都会新建一个String对象

使用orElseThrow()

这个针对阻塞性的业务场景比较合适,例如没有从上游获取到用户信息,下面的所有操作都无法进行,那此时就应该抛出异常。正常的写法是先判空,再手动throw异常,现在可以集成为一行:

    public User testThrow(User user) throws Exception {
        return Optional.ofNullable(user).orElseThrow(Exception::new);

不为null则执行时,使用ifPresent

这点没有性能上的优势,但可以使代码更简洁:

//之前是这样的
if (status.isPresent()) {
    System.out.println("Status: " + status.get());
status.ifPresent(System.out::println);

有些简单明了的方法,完全没有必要增加Optional来增加复杂性。

public String fetchStatus() {
    String status = getStatus() ;
    return Optional.ofNullable(status).orElse("PENDING");
//判断一个简单的状态而已
public String fetchStatus() {
    String status = ... ;
    return status == null ? "PENDING" : status;

使用map从Optional对象中提取和转换值

从对象中提取信息是一种比较常见的模式。比如,你可能想从insurance公司对象中提取公司的名称。提取名称之前,你需要检查insurance对象是否为null,代码如下:

        String name = null;
        if (insurance != null) {
            name = insurance.getName();

为了支持这种模式,Optional提供了一个map方法。它的工作方式如下:

        final Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
        final Optional<String> name = optInsurance.map(Insurance::getName);

从概念上,这与流的map方法相差无几。map操作会将提供的函数应用于流的每个元素。你可以把Optional对象看成一种特殊的集合数据,它至多包含一个元素。如果Optional包含一个值,那函数就将该值作为参数传递给map,对该值进行转换。如果Optional为空,就什么也不做。下图展示把一个正方形转换为三角形的函数,分别传递给正方形和Optional正方形流的map方法之后的结果。
在这里插入图片描述

使用flatMap链接Optional对象

可以把flatMap理解为打平 Optional<Optional>为Optional。
由于我们刚刚学习了如何使用map,你的第一反应可能是我们可以利用map重写之前的代码,如下所示:

    @Data
    static class Insurance {
        private String name;
    @Data
    static class Car {
        private Optional<Insurance> insurance;
    @Data
    static class Person {
        private Optional<Car> car;
        final Optional<Person> optPerson = Optional.ofNullable(person);
        final Optional<String> name = 
        		optPerson.map(Person::getCar)
                .map(Car::getInsurance)
                .map(Insurance::getName);

不幸的是,这段代码无法通过编译。因为optPerson是Optional类型的变量,调用map方法没问题,但getCar返回的是一个Optional类型的对象,这意味着map操作的结果是一个Optional<Optional>类型的对象。因
此,它对 getInsurance 的调用是非法的,因为最外层的 optional 对象包含了另一个 optional
对象的值,而它当然不会支持 getInsurance 方法。
在这里插入图片描述
所以,我们该如何解决这个问题呢?让我们再回?一下你??在流上使用过的模式:
flatMap 方法。使用流时, flatMap 方法接受一个函数作为参数,这个函数的返回值是另一个流。
这个方法会应用到流中的每一个元素,最终形成一个新的流的流。但是 flagMap 会用流的内容替
换每个新生成的流。换句话说,由方法生成的各个流会被合并或者?平化为一个单一的流。这里
你希望的结果其实也是类似的,但是你想要的是将两层的 optional 合并为一个。
在这里插入图片描述
因此我们可以使用下面代码获取car的保险公司名称:

        final Optional<Person> optPerson = Optional.ofNullable(person);
        final String name = 
        		optPerson.flatMap(Person::getCar)
                .flatMap(Car::getInsurance)
                .map(Insurance::getName)
                .orElse("Unknow");

在这里插入图片描述
截止目前为止,返回的Optional可能有两种情况:如果调用链上的任何一个方法返回一个空的Optional,那么结果就为空,否则返回的值就是你期望的保险公司的名称。

使用filter剔除特定的值

你经常需要调用某个对象的方法,查看它的某些属性。比如,你可能需要查保险公司的名
称是否为“Cambridge-Insurance”。为了以一种安全的方式进行这些操作,你首先需要确定引用指
向的 Insurance 对象是否为 null ,之后再调用它的 getName 方法,如下所示:

Insurance insurance = ...;
if(insurance != null && "CambridgeInsurance".equals(insurance.getName())){
System.out.println("ok");

使用 Optional 对象的 filter 方法,这段代码可以重构如下:

Optional<Insurance> optInsurance = ...;
optInsurance.filter(insurance ->  "CambridgeInsurance".equals(insurance.getName()))
			.ifPresent(x -> System.out.println("ok"));

filter 方法接受一个谓词作为参数。如果 Optional 对象的值存在,并且它符合谓词的条件,
filter 方法就返回其值;否则它就返回一个空的 Optional 对象。如果你还记得我们可以将
Optional 看成最多包含一个元素的 Stream 对象,这个方法的行为就非常清晰了。如果 Optional
对象为空,它不做任何操作,反之,它就对 Optional 对象中包含的值施加谓词操作。如果该操
作的结果为 true ,它不做任何改变,直接返回该 Optional 对象,否则就将该值过滤掉,将
Optional 的值置空。

异常与Optional的对比

由于某种原因,函数无法返回某个值,这时除了返回 null ,Java API比较常见的替代做法是
抛出一个异常。这种情况比较典型的例子是使用静态方法 Integer.parseInt(String) ,将
String 转换为 int 。在这个例子中,如果 String 无法解?到对应的整型,该方法就?出一个
NumberFormatException 。最后的效果是,发生 String 无法转换为 int 时,代码发出一个遭遇
非法参数的信号,唯一的不同是,这次你需要使用 try / catch 语句,而不是使用 if 条件?断来
控制一个变量的值是否非空。
你也可以用空的 Optional 对象,对遭遇无法转换的 String 时返回的非法值进行建模,这时
你期望 parseInt 的返回值是一个 optional 。我们无法修改最初的Java方法,但是这无?我们进
行需要的改进,你可以实现一个工具方法,将这部分逻辑?装于其中,最终返回一个我们希望的
Optional 对象,代码如下所示。

public static Optional<Integer> stringToInt(String s) {
	try {
		return Optional.of(Integer.parseInt(s));
	} catch (NumberFormatException e) {
		return Optional.empty();

我们的建议是,你可以将多个类似的方法?装到一个工具类中,让我们称之为 Optiona-
lUtility 。通过这种方式,你以后就能直接调用 OptionalUtility.stringToInt 方法,将
String 转换为一个 Optional 对象,而不再需要记得你在其中封装了笨拙的
try / catch 的逻辑了。

Optional的出现使Java对null的表达式更进一步,希望大家能够掌握API的使用方法和原理,合理使用可以避免大量的NPE,节省大量的人力物力。
Optional类支持多次方法,比如map、flatMap、filter,它们在概念上与Stream类中对应的方法十分相似。

概述Optional的本意是为了避免null带来的错误,为我们提供一个可以明确表示空值的有限的机制。基础理解Optional是一个容器,用于放置可能为空的值,它可以合理而优雅的处理null。Jdk1.8后,jdk新增了Optional来表示空结果。其实本质上什么也没有变,只是增加了一个表达式。Optional表示空的静态方法为Optional.empty(),跟null有什么本质区别吗?看它的实现,Optional中的value就是null,只不过包了一层Optional,所以说它其实是个容器。用 1.1.2 Lambda表达式可使用的变量 3 1.1.3 lambda表达式中的this概念 3 1.2 函数式接口- Consumer/Predicate/Function/Supplier 4 1.1.4 什么是函数式接口 4 1.1.5 Consumer接口 5 1.1.6 Function接口 6 1.1.7 Supplier接口 8 1.1.8 Predicate接口 9 1.3 方法引用和构造器 12 1.2.1 方法引用 12 1.2.2 构造器 12 1.4 Stream语法 13 1.3.1 入门案例 13 1.3.2 怎么得到Stream 15 1.3.3 转换Stream 16 1.3.4 汇聚Stream 19 2 Optional类 20 2.1 创建Optional容器 21 2.2 容器简单方法 22 2.3 容器进阶方法 23 1.3.5 ifPresent方法 23 1.3.6 orElseGet和orElseThrow方法 23 1.3.7 filter方法 24 1.3.8 map方法 24 1.3.9 flatMap方法 25 2.4 总结 25 1、Optional介绍 Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回对象Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。 Optional 类的引入很好的解决空指针异常。 2、构建Optional 构建一个Optional对象;方法有:empty( )、of( )、ofNullable( ) // returning Value With get() @Test public void givenOptional_whenGetsValue_thenCorrect() { Optional<String> opt = Optional.of("HoneyMoose"); String name...
1、Optional 介绍 传统的写代码方式经常会遇到NullPointerException,这就需要我们在代码中经常判空。而判空的写法又会显得很累赘,这里就可以用到Optional来简化代码。 Optional是在java.util包下的一个用于代替null的一个工具类。 import java.util.Optional; Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。 Optional 类是一个可以为n
Optional.of(T t):创建一个 Optional 实例,当 t为null时抛出异常 Optional.ofNullable(T t):创建一个 Optional 实例,但当 t为null时不会抛出异常,而是返回一个空的实例 @Test public
你好,我是看山。 Java8 中引入了一个特别有意思类:Optional,一个可以让我们更加轻松的避免 NPE(空指针异常,NullPointException)的工具。 很久很久以前,为了避免 NPE,我们会写很多类似if (obj != null) {}的代码,有时候忘记写,就可能出现 NPE,造成线上故障。在 Java 技术栈中,如果谁的代码出现了 NPE,有极大的可能会被笑话,这个异常被很多人认为是低级错误。Optional的出现,可以让大家更加轻松的避免因为低级错误被嘲讽的概率。 定义示例数据.
The request client is not a secure context and the resource is in more-private address ... 21048