优雅的链式非空判断--Optional

Optional是jdk1.8引入的类型,Optional是一个容器对象,它包括了我们需要的对象,使用isPresent方法判断所包含对象是否为空,isPresent方法返回false则表示Optional包含对象为空,否则可以使用get()取出对象进行操作。

public Person getPerson() {
        Person person = new Person();
        if (null == person) {
            return null;
        }
        return person;
    }

现在可以写成:

public Person getPerson() {
        Person person = new Person();
        return Optional.ofNullable(person).orElse(null);
}

其中Person类

@Data
public class Person {
    private String name;
    private Integer age;
}

Optional的优点是:

1、提醒你非空判断。

2、将对象非空检测标准化。

首先我们先打开Optional的内部,去一探究竟 先把几个创建Optional对象的方法提取出来

public final class Optional<T> {
   private static final Optional<?> EMPTY = new Optional<>();
   private final T value;
   //我们可以看到两个构造方格都是private 私有的
   //说明 我们没办法在外面去new出来Optional对象
   private Optional() {
        this.value = null;
    }
   private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }
    //这个静态方法大致 是创建出一个包装值为空的一个对象因为没有任何参数赋值
   public static<T> Optional<T> empty() {
        @SuppressWarnings(unchecked)
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
    //这个静态方法大致 是创建出一个包装值非空的一个对象 因为做了赋值
   public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }
    //这个静态方法大致是 如果参数value为空,则创建空对象,如果不为空,则创建有参对象
   public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }
 }

 1、我们可以看到两个构造方格都是private 私有的,说明我们没办法在外面去new出来Optional对象。

 2、静态方法:empty方法创建出一个包装值为空的对象。of方法创建出一个包装值非空的对象,ofNullable方法创建包装对象值可以为空也可以不为空的对象。

// 1、创建一个包装对象值为空的Optional对象
        Optional<String> optEmpty = Optional.empty();
        // 2、创建包装对象值非空的Optional对象
        Optional<String> optOf = Optional.of("optional");
        // 3、创建包装对象值允许为空也可以不为空的Optional对象
        Optional<String> optOfNullable1 = Optional.ofNullable(null);
        Optional<String> optOfNullable2 = Optional.ofNullable("optional");

常用方法:

//of():为非null的值创建一个Optional
Optional<String> optional = Optional.of("bam");
// isPresent(): 如果值存在返回true,否则返回false
optional.isPresent();           // true
//get():如果Optional有值则将其返回,否则抛出NoSuchElementException
optional.get();                 // "bam"
//orElse():如果有值则将其返回,否则返回指定的其它值
optional.orElse("fallback");    // "bam"
//ifPresent():如果Optional实例有值则为其调用consumer,否则不做处理
optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b"

使用案例1

//修改 
@Test 
public void testUpdate() { 
    Optional<CmsPage> optional = cmsPageRepository.findOne("5b17a34211fe5e2ee8c116c9"); 
    if(optional.isPresent()){ 
        CmsPage cmsPage = optional.get(); 
        cmsPage.setPageName("测试页面01"); 
        cmsPageRepository.save(cmsPage); 
    } 
}

使用案例2:判断从JSONObject中取出的字符串是否为空

String accessToken = (String) Optional.ofNullable(tokenResult.get("access_token")).orElse("");

实际开发中,这种判断比较常见。我们经常会对取出的值进行判断,如果为null,则赋值为空字符串,否则为本身。

Optional提供很多有用的方法,这样我们就不用显式进行空值检测。Optional 类的引入很好的解决空指针异常。

比如对于下面这一坨:我们如果采取传统的判空方式,可能代码要一大坨:

taiBaoCaiD02Request.getTaiBaoCaiD02Body().getTaiBaoCaiD02Param().getRegistNo()

但是如果使用了Optional之后可以为下面这种操作:

String orderNum = Optional.ofNullable(taiBaoCaiD05Request)
        .map(TaiBaoCaiD05Request::getTaiBaoCaiD05Body)
        .map(TaiBaoCaiD05Body::getTaiBaoCaiD05Param)
        .map(TaiBaoCaiD05Param::getOrderNum)
        .orElse("未指定orderNum");
Assert.notNull(taiBaoCaiD05Request);

瞬间就觉得清爽了好多,而且更加具有可读性,对于代码量来说也轻松了不少

Optional常见API:

1、Optional.get()方法(返回对象的值)

get()方法是返回一个option的实例值 源码:

public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

例子:

// 1、创建一个包装对象值为空的Optional对象
        Optional<String> optEmpty = Optional.empty();
        String s = optEmpty.get();

结果:java.util.NoSuchElementException: No value present

2、Optional.isPresent()方法(判读是否为空)

isPresent()方法就是会返回一个boolean类型值,如果对象不为空则为真,如果为空则false 源码:

public boolean isPresent() {
        return value != null;
    }

故在get之前需要调用isPresent方法进行判断

Optional<String> optOf = Optional.of("optional");
        if (optOf.isPresent()) {
            System.out.println(optOf.get());
        }

例2:

Person person = new Person();
        person.setAge(2);
        Optional<Person> optional = Optional.ofNullable(person);
        if (optional.isPresent()) {
            System.out.println("不为空");
        }

结果:不为空

3、Optional.ifPresent()方法(判读是否为空并返回函数)

 这个意思是如果对象非空,则运行函数体 源码:

public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

上面的代码可以简化如下:

Person person = new Person();
        person.setAge(2);
        Optional.ofNullable(person).ifPresent(p-> System.out.println(p.getAge()));

结果:2

 4、Optional.filter()方法(过滤对象)

 filter()方法大致意思是,接受一个对象,然后对他进行条件过滤,如果条件符合则返回Optional对象本身,如果不符合则返回空Optional

源码:

public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    //如果为空直接返回this
    if (!isPresent())
                return this; else
            //判断返回本身还是空Optional
    return predicate.test(value) ? this : empty();
}

简单实例:

Person person = new Person();
        person.setAge(2);
        Optional<Person> optional = Optional.ofNullable(person).filter(person1 -> person1.getAge() > 1);
        optional.ifPresent(p -> System.out.println(p.getAge()));

结果:2

5、Optional.map()方法(对象进行二次包装)

 map()方法将对应Funcation函数式接口中的对象,进行二次运算,封装成新的对象然后返回在Optional中 源码:

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        //如果为空返回自己
        if (!isPresent())
            return empty();
        else {
        //否则返回用方法修饰过的Optional
            return Optional.ofNullable(mapper.apply(value));
        }
    }

例子

Person person = new Person();
        person.setAge(2);
        Integer integer = Optional.ofNullable(person).map(Person::getAge).orElse(null);
        System.out.println(integer);

结果:2

6、Optional.orElse()方法(为空返回对象)

常用方法之一,这个方法意思是如果包装对象为空的话,就执行orElse方法里的value,如果非空,则返回写入对象 源码:

public T orElse(T other) {
    //如果非空,返回value,如果为空,返回other
    return value != null ? value : other;
}

7、Optional.orElseGet()方法(为空返回Supplier对象)

这个与orElse很相似,入参不一样,入参为Supplier对象,为空返回传入对象的.get()方法,如果非空则返回当前对象 源码:

Person person = new Person();
        person.setAge(2);
        Optional<Supplier<Person>> sup=Optional.ofNullable(Person::new);
//调用get()方法,此时才会调用对象的构造方法,即获得到真正对象
        Person person1 = Optional.ofNullable(person).orElseGet(sup.get());
        System.out.println(person1);

结果:Person(name=null, age=2)

Person person = null;
        Optional<Supplier<Person>> sup=Optional.ofNullable(Person::new);
//调用get()方法,此时才会调用对象的构造方法,即获得到真正对象
        Person person1 = Optional.ofNullable(person).orElseGet(sup.get());
        System.out.println(person1);

结果:Person(name=null, age=null)

8、Optional.orElseThrow()方法(为空返回异常)

这个我个人在实战中也经常用到这个方法,方法作用的话就是如果为空,就抛出你定义的异常,如果不为空返回当前对象,在实战中所有异常肯定是要处理好的,为了代码的可读性。源码:

 public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

实例:这个就贴实战源码了

Person person = null;
        Optional.ofNullable(person).orElseThrow(() -> new RuntimeException("没有查询到相关数据"));

结果:

java.lang.RuntimeException: 没有查询到相关数据

Optional API的应用

善用 Optional 可以使我们代码中很多繁琐、丑陋的设计变得十分优雅。

使用 Optional,我们就可以把下面这样的代码进行改写。

public static String getName(User u) {
    if (u == null || u.name == null)
        return "Unknown";
    return u.name;
}

不过,千万不要改写成这副样子。

public static String getName(User u) {
    Optional<User> user = Optional.ofNullable(u); // 使用Optional包装
    if (!user.isPresent())
        return "Unknown";
    return user.get().name;
}

这样改写非但不简洁,而且其操作还是和第一段代码一样。无非就是用 isPresent 方法来替代 u==null。这样的改写并不是 Optional 正确的用法,我们再来改写一次。

public static String getName(User u) {
    return Optional.ofNullable(u)
                    .map(user->user.name)
                    .orElse("Unknown");
}

这样才是正确使用 Optional 的姿势。那么按照这种思路,我们可以安心的进行链式调用,而不是一层层判断了。看一段代码:

public static String getChampionName(Competition comp) throws IllegalArgumentException {
    if (comp != null) {
        CompResult result = comp.getResult();
        if (result != null) {
            User champion = result.getChampion();
            if (champion != null) {
                return champion.getName();
            }
        }
    }
    throw new IllegalArgumentException("The value of param comp isn't available.");
}

让我们看看经过 Optional 加持过后,这些代码会变成什么样子。

public static String getChampionName(Competition comp) throws IllegalArgumentException {
    return Optional.ofNullable(comp)
            .map(Competition::getResult)  // 相当于c -> c.getResult(),下同
            .map(CompResult::getChampion)
            .map(User::getName)
            .orElseThrow(()->new IllegalArgumentException("The value of param comp isn't available."));
}

还有很多不错的使用姿势,比如字符串为空则不打印可以这么写:

string.ifPresent(System.out::println);

Optional 的魅力还不止于此,Optional 还有一些神奇的用法,比如 Optional 可以用来检验参数的合法性。

public void setName(String name) throws IllegalArgumentException {
    this.name = Optional.ofNullable(name)
                        .filter(User::isNameValid)
                        .orElseThrow(()->new IllegalArgumentException("Invalid username."));
}

 这样写参数合法性检测,应该足够优雅了吧。

 不过这还没完,上面的两个例子其实还不能完全反应出 Optional 的设计意图。事实上,我们应该更进一步,减少 Optional.ofNullable 的使用。为什么呢?因为 Optional 是被设计成用来代替 null 以表示不确定性的,换句话说,只要一段代码可能产生 null,那它就可以返回 Optional。而我们选择用 Optional 代替 null 的原因,是 Optional 提供了一个把若干依赖前一步结果的处理结合在一起的途径。

 Optional应用建议

Optional 就像一个处理不确定性的管道,我们在一头丢进一个可能是 null 的东西(接口返回结果),经过层层处理,最后消除不确定性。Optional 在过程中保留了不确定性,从而把对 null 的处理移到了若干次操作的最后,以减少出现 NPE 错误的可能。于是,Optional 应用的建议也呼之欲出了:

  1. 适用于层级处理(依赖上一步操作)的场合
  2. 产生对象的方法若可能返回 null,可以用 Optional 包装。
  3. 尽可能延后处理 null 的时机,在过程中使用 Optional 保留不确定性。
  4. 尽量避免使用 Optional 作为字段类型。
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值