用Java optional模仿Kotlin? - Welsh


让我们先来看看Optional(在Java8中引入)是做什么的?
Optional是对一个对象的封装,提供了安全处理底层对象的方法,同时避免了可怕的空指针异常。
这提供了与Kotlin的nullable数据类型基本相同的行为,并允许Java有办法模仿Kotlin的nullable数据类型。

而Java没有的是一个与Kotlin相匹配的非空类型:你不能阻止将null赋值给一个变量。
那么我们怎么做呢?
我们可以假设所有不是Optional的东西都是非空的。
最好是为IDE和编译器的警告做注解,以帮助我们(可能的话还可以使用Lombok在运行时尽可能地执行它):

@Nonnull
String constructMyMessage(@Nonnull final String message,
                          @Nonnull final Optional<String> name) {
  final String userName = name.orElse("unknown person!");
  return userName +
" said " + message;
}

/**
  NOTE: This method illustrates handling an optional input and return.
  However, this could be better handled by checking the name optional _before_ calling this method
  (and then woudln't need an optional return).
*/

@Nonnull
Optional<String> maybeConstructAMessage(@Nonnull final String message,
                                        @Nonnull final Optional<String> name) {
  return name.map(evaluatedName -> evaluatedName +
" said " + message);
}

注意:所有的Java返回类型和参数都被标记为@Nonnull - 意味着我们不希望在代码中看到任何null。
相反,可以使用Optional.empty();这是一个更明确的指示,即不打算存在任何值,而只是缺少。
 
Kotlin:

fun constructMyMessage(message: String, name: String?): String {
  val userName = name ?: "unknown person"
  return
"$userName said $message"
}

/**
  NOTE: This method illustrates handling an optional input and return.
  However, this could be better handled by checking the name optional _before_ calling this method
  (and then woudln't need an optional return).
*/

fun maybeConstructAMessage(message: String, name: String?): String? {
    return name?.let {
"$it said $message" }
}

 
对于Java来说,这将使Optional Everywhere。
当一个值可能为空时,用一个Optional来包装它,适用于返回类型、方法参数和成员变量。
 
通过这种方法,你可以避免大多数你可能需要写null的情况。相反,你可以使用Optional类所提供的方法来操作底层的值。一些有用的方法。

  • ifPresent(Consumer<? super T> consumer)如果值是存在的并且不是空的,就执行consumer 。
  • map(Function<? super T, ? extends U> mapper)使用mapper将底层值转换为一个新的类型。
  • orElse(T other) 如果底层值为空,则返回底层值或其他。这允许你保证检索到一个非空值。

还有很多可以用Optionals做的事情,可以在Java 8、11和17文档中找到。
我曾经是只把Optional作为返回类型的阵营,但在与Kotlin和Swift一起工作后,我已经改用Optional了。
 
这如何适用于集合Collections?
在可能的情况下,如果不需要区分空的情况和空的情况,最好使用空的集合而不是空的集合。如果需要区分空集和空集(比如PATCH请求),可以使用一个Optional来区分集合的两种状态。
 
遵循这种方法,你的代码库将更接近于Kotlin,而且还有一个额外的好处,那就是消除了大部分(如果不是全部)你可能遇到的十亿美元的错误的情况。