【问题标题】:Why isn't optional used for instance variables?为什么不可选用于实例变量?
【发布时间】:2019-06-05 08:56:02
【问题描述】:

我已经阅读了很多关于应该使用Optional 的案例。

我读过的很多页面都说Optional 不应该用于私有实例变量,而应该由getter 返回。

我原以为将私有实例变量作为可选变量仍然有用。如果有人查看我的代码,他们可以看到一个值可以为空,而不必检查文档以查看是否可以返回 null。

在 Scala 中,从不使用 null,它只是为了与 Java 进行互操作。如果值可以为空,建议始终使用可选项。这种方法对我来说更有意义。

这里有一个页面提到它:

https://blog.joda.org/2015/08/java-se-8-optional-pragmatic-approach.html

这是示例代码。

private final String addressLine;  // never null
private final String city;         // never null
private final String postcode;     // optional, thus may be null

// normal getters
public String getAddressLine() { return addressLine; }
public String getCity() { return city; }

// special getter for optional field
public Optional<String> getPostcode() {
  return Optional.ofNullable(postcode);
}

我能看到的唯一优点是,如果您想序列化对象,现在可以实现,因为它没有将可选存储在变量中。

缺点是在检查 getter 的返回类型之前,您不知道 postcode 可能为 null。如果您不熟悉代码,您可能会错过这个添加扩展类,从而导致空指针异常。

这是一个关于 Scala 的Option 的问题。

When to use Option

为什么 Java 和 Scala 在应该如何使用 optional 方面存在差异?

【问题讨论】:

  • 虽然我不了解 Scala 实践,但我还没有真正看到过使用 Optional 作为实例变量的 Java 示例最终可以为您带来任何好处。根据我的经验,某些方法读取可空变量的值可能很方便,但只是将负担转移到设置它的方法上。也就是说,我已经看到带有 Optional 实例变量的代码可以毫无问题地工作(我一开始就不会编写它)。
  • 我从使用它中看到的主要优点是,对于刚接触代码的人来说,很清楚某些内容可能为空/null。它还使您更多地考虑如果变量为空会发生什么,因为有时人们会忘记变量可以为空。无论如何,这是 Scala 中采用的方法。在示例代码中,除非您检查 getter 返回的内容,否则您不会知道变量可能为 null。
  • 非常重要的区别是,Optional 是不可序列化的,所以如果你创建了任何带有Optional 字段的类,你将无法序列化它。来自 Scala 的 Option 是可序列化的,正如您所注意到的,创建 Option 字段是常见的做法。正如我需要提到的,来自 VavrOption 是可序列化的,所以如果你想用Option scala-style 创建字段,只需使用 Vavr.
  • IMO 主要是封装问题。当您将方法声明为返回 Optional 时,很明显可能会出现空结果,而无需查看代码甚至文档。如果您正在查看成员变量,那么您已经在查看代码
  • Java Optional 并非设计用于成员变量。有许多问题 - 例如,如果它打算用于成员变量,它应该实现 Serializable。请参阅:dzone.com/articles/optional-anti-patterns 和:dolszewski.com/java/java-8-optional-use-cases 这是经过深思熟虑的设计选择。

标签: java scala optional


【解决方案1】:

并非所有 Java 开发人员都同意您描述的方法。请查看 Lombok 的创建者post

我猜想在 Java 中使用 Optional 的不同方法的原因是 Java 社区在 Java 8 之前一直没有它,所以大多数人习惯于 null。一方面,许多新的 API(如 Stream 中的 findAny)返回 Optional,但仍有许多标准库方法只返回 null,因此您始终必须记住用Optional.ofNullable 包装你的函数调用或检查值是否不为空。

Optional 已添加到 Java 8,但不鼓励将其用作类字段,因为 Optional 没有实现 Serializable(并且 Java 的序列化被许多框架或系统(如 Akka)用作默认序列化引擎, Spark、Kafka 等)。

另一方面,Option 与 Scala 标准库的绑定非常紧密。 据我所知,没有 Scala 的标准库 API 返回 null,而是返回 Option,并且完全不鼓励在 Scala 代码中使用 null。如果 nullused,您甚至可以将项目配置为编译失败。

Option 也是Serializable,其通常的做法是用作可以为空的值的类字段。

如果您想在 Java 代码中使用类似的方法,请查看 Vavr 中的 Option。它是可序列化的,因此可以安全地用作字段,并且它还有两个子类NoneSome(类似于Scala 的Option),因此可以在Vavr 的模式匹配中使用:

Match(option).of(
    Case($Some($()), "defined"),
    Case($None(), "empty")
);

【讨论】:

    【解决方案2】:

    在 Scala 中,Option 紧密集成到语言的 API 中。

    表示可选值。 Option 的实例要么是 scala.Some 的实例,要么是对象 None。 使用 scala.Option 实例最惯用的方式是将其视为集合或 monad,并使用 map、flatMap、filter 或 foreach。

    从上面的引用中可以看出,那里没有null 的解释,因为它应该用作单子或集合。

    在 Java 中,Optional 用于将我们从 NullPointerException 情况中拯救出来,将其用作:

    一个容器对象,它可能包含也可能不包含非空值。如果存在值,isPresent() 将返回 true,而 get() 将返回该值。

    通过使用? safe call,可以非常清楚地向用户显示变量是否为空的编程语言是Kotlin,并向您显示编译错误:

    var a: String = "abc"
    a = null // compilation error
    
    var b: String? = "abc"
    b = null // ok
    print(b)
    

    【讨论】:

      【解决方案3】:

      有一点,Optional 和 Stream 一样,是一个迭代的 «event» 类,不值得用作“真实”对象中的字段。

      然后是不可序列化的缺点(可以解决)。

      但是我的观点是,一些变量可能是可选,基数为 0 和 1。 正如 list 也是有效的。对于List 字段,趋势是(恕我直言)它最初不能为空,而是始终有一个(空)列表。

      在相同的编码风格中,Optional 确保只使用安全访问,尤其是可以以链式风格进行映射:

      Optional<TextField> t = ...
      
      String s = t.map(t::getText).orElse("N/A");
      Optional<String> u = t.map(t::getText);
      t.ifPresent(s -> { ... });
      

      尤其是 ifPresent 确保不会意外使用 null。

      可选是一个有价值的规范。

      【讨论】:

        【解决方案4】:

        不使用Optional 作为实例变量的一个原因是Optional 引用本身很容易成为null。这违反了围绕Optional 的隐含合同,即您永远不应创建或遇到nullOptional 的引用。

        class Foo {
            Optional<Bar> bar;
        }
        
        Foo f = new Foo();
        if (f.bar.isPresent()) { // NullPointerException
            ...
        }
        

        这是在 Java 中创建具有Optional 字段的类的最简单方法。从某种意义上说,该语言鼓励您编写这样的代码。

        另一方面,在 Scala 中,定义Foo 的最简单方法是

        case class Foo(bar: Option[Bar])
        

        这很容易做到

        case class Foo(bar: Option[Bar] = None)
        

        两者都避免了 NPE。

        【讨论】:

        • 如果您正在编写 java,那么您已经咬住了冗长的子弹,如果您阅读了作者自己写的内容,那么根据经验法则,这不是他们想要的。
        • 好吧,stackoverflow.com/a/26328555/870178,但这家伙实际上很好地代表了我的观点:blog.codefx.org/java/…
        猜你喜欢
        • 2016-05-27
        • 1970-01-01
        • 2011-06-06
        • 2012-08-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-20
        相关资源
        最近更新 更多