Optional中orElse与orElseGet的区别

0. 写在前面

这篇文章的目的是为了说明orElse可能导致NullPointerException,当orElse的参数是间接计算得来的时候。虽然这种说法有点牵强(因为并不是orElse导致了空指针异常),但是使用orElseGet确实可以避免这种情况。至于两者的区别,这是文章的补充内容,所以你可以按需取用。

1. orElseorElseGet介绍与使用

一般而言,我们使用orElse或是orElseGet均是来做使用Optional时的收尾工作,比如:

Optional.ofNullable(aVariableThatMayBeIsNull)
        // 一些其它操作,比如 .map()
        .orElse(defaultValue); // 或是 .orElseGet(aFunctionUsedToComputeDefaultValue)

两者的明显(也是唯一)区别是前者需要传递的参数是一个值(通常是为空时的默认值),后者传递的是一个函数。我们看一下源代码:

/**
 * Return the value if present, otherwise return {@code other}.
 */
public T orElse(T other) {
    return value != null ? value : other;
}

/**
 * Return the value if present, otherwise invoke {@code other} and return
 * the result of that invocation.
 */
public T orElseGet(Supplier<? extends T> other) {
    return value != null ? value : other.get();
}

简单解释为,我们使用Optional包装的变量如果不为空,返回它本身,否则返回我们传递进去的值。orElseGet参数为Supplier接口,它是一个函数式接口,它的形式是这样的:() -> { return computedResult },即入参为空,有返回值(任意类型的)。推荐浏览下java.util.function包下的其它常用接口,比如ConsumerFunctionPredicate

2. 更进一步:两者的区别

我们可能考虑的问题是:何时使用orElse和何时使用orElseGet?看起来可以使用orElseGet的时候,使用orElse也可以代替(因为Supplier接口没有入参),而且使用orElseGet还需要将计算过程额外包装成一个 lambda 表达式。

一个关键的点是,使用Supplier能够做到懒计算,即使用orElseGet时。它的好处是,只有在需要的时候才会计算结果。具体到我们的场景,使用orElse的时候,每次它都会执行计算结果的过程,而对于orElseGet,只有Optional中的值为空时,它才会计算备选结果。这样做的好处是可以避免提前计算结果的风险

3. 场景举例

举个例子,或许更清楚一些:

class User {
    // 中文名
	private String chineseName;
	// 英文名
	private EnglishName englishName;
}

class EnglishName {
    // 全名
    private String fullName;
    // 简写
    private String shortName;
}

假如我们现在有User类,用户注册账号时,需要提供自己的中文名或英文名,或都提供,我们抽象出一个EnglishName类,它包含英文名的全名和简写(因为有的英文名确实太长了)。现在,我们希望有一个User#getName()方法,它可以像下面这样实现:

class User {
    // ... 之前的内容
    public String getName1() {
        return Optional.ofNullable(chineseName)
                .orElse(englishName.getShortName());
    }
    public String getName2() {
        return Optional.ofNullable(chineseName)
                .orElseGet(() -> englishName.getShortName());
    }
}

我写了两个版本,分别使用orElseorElseGet。现在,你可以看出getName1()方法有什么风险了吗?它会出现空指针异常吗?

答案是:是的。当用户只提供了中文名时,此时englishName属性是null,但是在orElse中,englishName.getShortName()总是会执行。而在getName2()中,这个风险却没有。

4. 真实案例

或许上面那个例子还不是很清楚,我们看一个真实的案例分页插件报错The jdbcUrl is Null, Cannot read database type #2172
其中提到的一条语句为:DbType dbType = (DbType)Optional.ofNullable(this.dbType).orElse(JdbcUtils.getDbType(connection.getMetaData().getURL()));,没错,问题是出在orElse那里。

5. 总结

这篇文章分析了常用的Optional中的orElseorElseGet方法,比较了两者的区别,说明了使用orElse时的风险,并提供了一个简朴的例子,我的收获是从4. 真实案例中学到的。

总结下来,使用Optional时,当备选值是通过某个计算过程得到的时候,你都应该小心这个计算过程是否有风险,并在orElseorElseGet之间做一个权衡。

  • 17
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Java 8Optional类被引入来解决空指针异常。Optional是一个容器对象,它可能包含某个值或者不包含。使用Optional可以避免我们在if语句进行null的判断。在Optional,有三个方法:orElse,orElseGet和orElseThrow,它们的作用都是当Optional包含的值为空时,提供一个默认值。 1. orElse orElse方法是Optional的一个方法,它的作用是在Optional包含的值为空时,提供一个默认值。如果Optional包含的值不为空,则返回该值。orElse方法的语法如下: ``` public T orElse(T other) ``` 其,other是一个T类型的对象,表示当Optional包含的值为空时,需要返回的默认值。orElse方法的返回值类型是T类型。 下面是一个例子: ``` Optional<String> optional = Optional.empty(); String result = optional.orElse("default"); System.out.println(result); //输出default ``` 在这个例子Optional包含的值为空,所以使用了orElse方法提供了一个默认值"default"。 2. orElseGet orElseGet方法也是Optional的一个方法,它的作用和orElse方法类似,都是在Optional包含的值为空时,提供一个默认值。但是orElseGet方法提供的默认值是通过一个Supplier对象来生成的,而不是直接提供的。如果Optional包含的值不为空,则返回该值。orElseGet方法的语法如下: ``` public T orElseGet(Supplier<? extends T> other) ``` 其,other是一个Supplier对象,它的get()方法返回一个T类型的对象,表示当Optional包含的值为空时,需要返回的默认值。orElseGet方法的返回值类型是T类型。 下面是一个例子: ``` Optional<String> optional = Optional.empty(); String result = optional.orElseGet(() -> "default"); System.out.println(result); //输出default ``` 在这个例子Optional包含的值为空,所以使用了orElseGet方法提供了一个默认值"default"。 3. orElseThrow orElseThrow方法也是Optional的一个方法,它的作用是在Optional包含的值为空时,抛出一个异常。如果Optional包含的值不为空,则返回该值。orElseThrow方法的语法如下: ``` public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X ``` 其,exceptionSupplier是一个Supplier对象,它的get()方法返回一个X类型的异常对象,表示当Optional包含的值为空时,需要抛出的异常。orElseThrow方法的返回值类型是T类型。 下面是一个例子: ``` Optional<String> optional = Optional.empty(); try { String result = optional.orElseThrow(() -> new Exception("Optional is empty")); } catch (Exception e) { e.printStackTrace(); } ``` 在这个例子Optional包含的值为空,所以使用了orElseThrow方法抛出了一个异常。 总结:orElse、orElseGet和orElseThrow都是在Optional包含的值为空时,提供一个默认值。orElse提供的默认值是直接提供的,orElseGet提供的默认值是通过一个Supplier对象来生成的,orElseThrow抛出一个异常。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值