【问题标题】:Is get() on Java's Optional hazardous?Java 的 Optional 上的 get() 有危险吗?
【发布时间】:2017-01-09 14:39:35
【问题描述】:

在函数式语言中,对可选类型使用模式匹配是很常见的:

let result = match myOptional with 
             | Some x -> x * 2
             | None -> 0

这对程序员很有帮助,因为编译器会检查模式匹配是否完整。

但是,在我看到的 Java 的 Optional 示例中,使用了 isPresentget

Integer result;
if (myOptional.isPresent()) {
    result = myOptional.get() * 2;
} else {
    result = 0;
}

对我来说,这违背了Optional 的目的。编译器不检查以确保正确实现if 的两个分支,并且生成的代码与使用null 的等效代码相比没有额外的保证。

这种设计选择不利于安全,那么为什么标准库提供get 函数而不仅仅是match

【问题讨论】:

  • 您希望编译器检查什么“正确性”?确定分配确保您在使用之前已分配result;我想不出你还想要什么。 (我可能只是在这里想象失败......)
  • 是的,它可以帮助程序员避免 NPE。
  • “它可以帮助程序员避免 NPE” 但是在这种情况下你会得到什么 NPE?
  • 这只是一个例子。

标签: java functional-programming


【解决方案1】:

这是通过the Optional::map method 完成的。你的例子可以写成:

Integer result = myOptional.map(i -> i * 2).orElse(0);

关于 get 方法,已经有 a discussion 弃用它 - 我不确定是否已经做出决定。

【讨论】:

    【解决方案2】:

    上面的 F# sn-p 更安全,因为它确保结果总是被初始化。忽略这两种情况中的任何一种都是编译时错误。

    Java 也是如此:Java 使用 definite assignment 来确保初始化局部变量和最终变量。

    请注意,这与Optional 无关,特别是:任何时候编译器可以确定此类变量在使用之前可能尚未初始化,都会引发错误。

    例如:

    int result;
    if (someCondition) {
      result = 0;
    }
    System.out.println(result);  // result might not have been initialized.
    

    Ideone demo

    Ideone demo, using OP's code

    所以这个设计并没有什么“不安全”的地方(关于局部变量或最终变量的赋值)。

    【讨论】:

    • 干得好,试图理解 OP 的意思!
    • 不同意。初始化只是图片的一部分。编译器无法确保程序员没有意外混淆if 分支。作者可以写if (!opt.isPresent()) { result = opt.get() * 2; },这会在运行时失败。
    【解决方案3】:

    是的,通常您应该避免致电get()。然而,有时这是唯一令人满意的解决方案,因为 lambda 表达式的功能相当有限。以下是 lambdas 不能做的事情的列表:

    • 如果方法没有对应的throws 声明,则抛出检查异常
    • 在外部范围内修改可变变量
    • 从循环中中断(或继续)
    • 从方法返回

    考虑这个例子:

    if (opt.isPresent()) {
        int value = opt.get();
        if (value == 0) {
            throw new Exception("Catastrophical error!");
        } else {
            // ...
        }
    }
    

    由于Consumer 不支持检查异常,所以不能简单地用 lambdas 重写这段代码。

    lambda 的其他缺点:

    • 更长的堆栈跟踪
    • 更难调试
    • 需要内存分配(对于性能关键型应用程序很重要)

    所以,最终的答案是:尝试编写正确且功能强大的代码。由于get() 可能会影响代码的正确性,因此请尽可能避免使用它。但是,如果您遇到 lambdas 的限制,请不要绞尽脑汁,只需使用 get()。当您的同事看到可读的 sn-p 代码而不是修改外部可变变量或在 lambda 中抛出已检查异常的丑陋解决方法时,他们会感谢您。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-16
      • 1970-01-01
      • 2011-12-31
      • 1970-01-01
      • 2015-03-17
      • 2018-10-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多