Java 8 开始新增的 Optional 类 - Optional 中的方法
fPresent() 的使用条件
ifPresent() 方法能够让我们在对对象进行下一步操作之前判断我们需要操作的对象是否为 Null,在没有 Optional 对象之前,我们通常使用下面的方法先进行判断:
if(name != null) {
System.out.println(name.length());
}
上面的程序逻辑是,首先判断 name 这个变量是不是为空,如果不为空的话,允许程序继续执行下一步。
这种判断方法不是很美观,代码也比较难看,更重要的是这种判断方法也是容易出错的。
有谁又能够保证我们在检查空,并且打印出变量后,这个变量不被再次使用呢,在这个变量再次使用的时候又有谁能够保证我们不会忘记空检查呢?
同时,有可能在程序的运行时导致空对象异常,NullPointerException。尤其是在程序因为输入的问题导致失败,无法启动的情况下,通常这种情况是因为程序本身没有被很好的设计和编码。
Optional 能够非常明确的处理可能为空的变量,这个是一种比较好的编码习惯。
让我们看看上面的代码在 Java 8 的环境下是如何进行实现的。
在常用的函数编程的情况下,我们在对象不进行空检查后使用函数式进行编程:
@Test
public void givenOptional_whenIfPresentWorks_thenCorrect() {
Optional<String> opt = Optional.of("HoneyMoose");
opt.ifPresent(name -> LOG.debug("{}", name.length()));
}
在上面的示例中,我们仅仅使用了 2 行代码就实现了第一种方法需要使用的 5 行代码。
第一行代码使用 Optional 对象来对我们的变量进行包装,第二行代码就对已经包装好的 Optional 对象进行相应的操作。
orElse() 方法来定义默认值
orElse() 这个方法被用来获取 Optional 实例中内部的值。
这个方法只需要 1 个参数,如果 Optional 对象中的值不为空的话,程序将会返回 Optional 对象中的值,否则将会使用 orElse 这个方法中输入参数的值来替代输出。
当然,你也可以在 orElse() 调用一个方法,至于和 orElseGet() 有什么不同,我们将会在后面进行说明。
考察下面的代码:
@Test
public void whenOrElseWorks_thenCorrect() {
String nullName = null;
String name = Optional.ofNullable(nullName).orElse("john");
assertEquals("john", name);
}
orElseGet() 方法来定义默认值
orElseGet() 和 orElse() 方法类似。
我们都知道,如果 Optional 为空的时候,如果使用 orElse() 方法,将会使用这个方法中输入的参数来替代返回,orElseGet() 就更近一步了。
orElseGet 提供的是一个函数式的接口,你可以在 orElseGet() 中使用函数编程,返回的结果就是这个函数进行运算后的结果。
@Test
public void whenOrElseGetWorks_thenCorrect() {
String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(() -> "john");
assertEquals("john", name);
}
orElse() 和 orElseGet() 方法的对比
和很多程序员一样,如果你是开始接触 Java 8 的话,你可能对 orElse() 和 orElseGet() 2 个方法之间的执行不同有所不了解,觉得这 2 个方法在功能上都是重复的。
事实上看起来就是这样的,但是在实际上还是有一些微妙的不同的。
如果你对这些细微的不同不够了解的话,有可能会严重影响你程序的执行效率。
简单来说就是其中定义的函数是否被执行的区别,不管前面对 Optional 的判断是否为 null, orElse() 中调用的方法都会被执行,orElseGet() 却不会。
首先,让我们在测试类中定义一个 getMyDefault() 方法,这个方法不使用任何参数,只是打印并且返回一个字符串:
public String getMyDefault() {
System.out.println("Getting Default Value");
return "Default Value";
}
然后我们分别使用 orElse() 和 orElseGet() 来进行调用这个方法,我们来看看有什么不同。
Optional 对象为空的情况
@Test
public void whenOrElseGetAndOrElseOverlap_thenCorrect() {
String text = null;
String defaultText = Optional.ofNullable(text).orElseGet(this::getMyDefault);
assertEquals("Default Value", defaultText);
defaultText = Optional.ofNullable(text).orElse(getMyDefault());
assertEquals("Default Value", defaultText);
}
正如我们所看到的,因为 Optional 对象为空,我们定义的函数都被调用了。
程序的输出如下,从程序的输出可以看出来,这 2 个方法的执行是相同的。
The side effect is:
Getting default value...
Getting default value...
Optional 对象不(NOT)为空的情况
使用上面相同的代码,但是这次不同的是,我们定义的 Optional 对象是不为空的
@Test
public void whenOrElseGetAndOrElseDiffer_thenCorrect() {
String text = "Text present";
LOG.debug("Using orElseGet:");
String defaultText = Optional.ofNullable(text).orElseGet(this::getMyDefault);
assertEquals("Text present", defaultText);
LOG.debug("Using orElse:");
defaultText = Optional.ofNullable(text).orElse(getMyDefault());
assertEquals("Text present", defaultText);
}
如上面的代码所展示的,我们需要判断的 Optional 对象已经不为空了,程序的输出如下所示:
Using orElseGet:
Using orElse:
Getting default value...
注意到 orElseGet() 方法在我们检查 Optional 对象不为空的时候,就不再调用 getMyDefault 这个方法。
然后我们再来看看 orElse() 这个方法,尽管 Optional 对象不为空,但是 orElse() 这个方法中调用的方法还是被执行了一次。
如果是创建对象的话,那么我们就创建了一个从来没有使用过的对象,在 JVM 的垃圾回收下,这个被创建的对象随后就被回收销毁了。
考虑一种极端的情况,如果我们定义的 getMyDefault() 方法不是创建对象并且销毁这么简单,假设我们需要进行数据库查询,或者 HTTP 访问等,这个将会导致程序有很大的开销。
通常我们都知道,在数据库查询的时候,创建数据库连接是很消耗资源的。
因此这就是这个 2 个方法在使用时候的区别,主要区别就在 Optional 对象不为空的情况。