1. 引言

在Java 8中,并没有像Groovy或Kotlin那样的内置Elvis(空合并)运算符。然而,我们可以利用方法引用和三元运算符来实现自己的Elvis运算符。本教程将探讨如何在Java 8中实现Elvis运算符。

2. 理解Elvis运算符

Elvis运算符在Groovy和Kotlin等语言中常用,它由?:符号表示,用于当原始值为null时提供默认值。

运算符会评估其左侧的表达式,如果它不为null,则返回该表达式的值。如果左侧表达式评估为null,则返回右侧的表达式。

例如,在Kotlin中,我们可以写val name = person.name?: "Unknown"来获取用户的名字,如果名字存在则返回,否则返回"Unknown"。

3. 使用三元运算符

三元运算符(?:)允许在表达式内进行简洁的if-else结构。虽然它并非真正的Elvis运算符,但也能实现类似的null检查和默认赋值。

设想一个场景,我们有一个从数据库获取用户名字的方法,如果找不到用户,该方法可能会返回null。传统上,我们会使用三元运算符进行null检查并指定默认值:

User user = new User("Baeldung"); // Simulate user object return from database
String greeting = (user != null && user.getName() != null) ? user.getName() : "Hello, Stranger";

assertEquals("Baeldung", greeting);

User user = new User(null);
String greeting = (user != null && user.getName() != null) ? user.getName() : "Hello, Stranger";

assertEquals("Hello, Stranger", greeting);

三元运算符为在表达式内处理null检查和默认赋值提供了简洁且表达式的解决方案。然而,对于嵌套的null检查,它就显得有些冗长:

String address = user != null ? user.getAddress() != null ? user.getAddress().getCity() : null : null;

4. 使用Optional

Java 8引入的Optional类是安全处理null引用的强大工具。它表示值的存在与否。

我们可以使用ofNullable()方法从可能为null的值创建一个Optional,然后链式调用map()操作,如果值存在则在其上执行操作。最后,我们使用orElse()指定在Optional为空时的默认值:

现在,我们想要将用户的名字转换为大写,如果存在的话,否则返回"Hello Stranger":

User user = new User("Baeldung");

String greeting = Optional.ofNullable(user.getName())
  .map(String::toUpperCase) // Transform if present
  .orElse("Hello Stranger");

assertEquals("BAELDUNG", greeting);

在这个代码中,Optional.ofNullable(user.getName())根据用户的名字创建一个Optional,处理了null的可能性。然后我们使用map(String::toUpperCase)在名字存在时将其转换为大写。最后,orElse("Hello Stranger")在名字为null时指定默认问候语:

User user = new User(null);

String greeting = Optional.ofNullable(user.getName())
  .map(String::toUpperCase)
  .orElse("Hello Stranger");

assertEquals("Hello Stranger", greeting);

这种方法促进了null安全性,避免了潜在的NullPointerException

5. 使用自定义方法

我们可以创建一组实用方法,接受目标对象和函数,如果目标对象不为null,则应用函数。甚至可以将这些方法串联起来,创建一系列的null合并操作。

为了创建一个模仿Elvis运算符的自定义实用方法,我们定义一个泛型方法,以处理不同类型的数据:

public static <T> T elvis(T value, T defaultValue) {
    return value != null ? value : defaultValue;
}

这个方法接受两个参数:一个需要检查是否为null的值和在值为null时返回的默认值。然后根据null检查返回valuedefaultValue

User user = new User("Baeldung");
String greeting = elvis(user.getName(), "Hello Stranger");
assertEquals("Baeldung", greeting);

user = new User(null);
String greeting = elvis(user.getName(), "Hello Stranger");
assertEquals("Hello Stranger", greeting);

使用像elvis()这样的自定义实用方法,相比嵌套的三元运算符,有多个优势。它通过封装null检查逻辑到单独的方法中,提高了代码组织性,增强了可读性和可维护性。

让我们看看这个例子:

User user = new User("Baeldung");
user.setAddress(new Address("Singapore"));

String cityName = elvis(elvis(user, new User("Stranger")).getAddress(), new Address("Default City")).getCity();

assertEquals("Singapore", cityName);

首先,我们检查user是否为null。如果为null,则返回一个新的User对象,其中默认值为"Stranger"。接下来,我们从user对象中获取address。如果getAddress()返回null,我们返回一个新的Address对象,其中默认城市名为"Default City":

User user = new User("Baeldung");
user.setAddress(null);

String cityName = elvis(elvis(user, new User("Stranger")).getAddress(), new Address("Default City")).getCity();

assertEquals("Default City", cityName);

这种使用elvis()方法的串联方式允许我们以简洁和可读的方式处理嵌套的null检查,确保我们的代码优雅地处理null情况,而不必求助于冗长的if-else结构。

6. 总结

在这篇文章中,我们在Java 8中使用Optional类和三元运算符实现了Elvis运算符。此外,我们创建了一个自定义实用方法elvis()来处理null检查和默认赋值。通过将逻辑封装在方法中,我们提升了代码可读性和可维护性,同时促进了代码重用。

如往常一样,示例代码可在GitHub上的项目中找到。