【问题标题】:Calling Optional#isPresent() in single line is reported as not called在单行中调用 Optional#isPresent() 报告为未调用
【发布时间】:2019-04-10 07:17:28
【问题描述】:

我运行 SonarQube 来检查我的代码,我发现了一个我不理解报告的错误的案例。

我的代码是:

private static final int BASE_ID = 100_000_000;
private boolean isValidId(Id id) {
    return id.asInteger().isPresent() && id.asInteger().get() >= BASE_ID;
}

方法asInteger返回Optional<Integer>

我从 sonarqube 得到的错误是 Call "Optional#isPresent()" before accessing the value. 在返回线中。

我知道代码没问题,因为如果第一部分为 false,if 的第二部分将不会被执行。我知道这可以通过.filter(..).isPresent() 解决,但我更喜欢这种方式。

任何想法为什么会发生这种情况?

【问题讨论】:

  • 我会说这只是因为那些代码检查工具并不总是完美地工作,并且一些错误只是误报。如果你改写成if(...) 肯定不会报错了
  • 如果第二次调用id.asInteger() 会返回空Optional
  • return id.asInteger().orElse(-1) >= BASE_ID; 呢?

标签: java java-8 sonarqube optional sonarqube-scan


【解决方案1】:

顺便说一句,你可以把它写成一条语句:

return id.asInteger()
         .map(x -> x >= BASE_ID)
         .orElse(false)

但声纳抱怨是因为在这种情况下它是误报。

【讨论】:

  • 是的,我知道我可以用不同的方法来解决它,就像我在问题本身中写的那样:.filter(..).isPresent() 但我更喜欢另一种方式,并想了解为什么会发生这种情况。我的意思是,正在调用 isPresent()...
  • 但是 SonarQube 无法知道每次调用 asInteger() 是否返回不同的值。所以第二次调用get()的时候,可能是一个空的Optional
  • 这不是误报 - Sonar 将其作为潜在问题强调是完全正确的
【解决方案2】:

Sonarqube 不能保证对 id.asInteger() 的两次调用返回相同的对象,例如因为多线程可能在两次调用之间改变了id 的值,所以它正确地说明了存在性没有经过充分的测试。

首先更改代码以分配给局部变量,以确保在同一对象上调用isPresent()get()

private boolean isValidId(Id id) {
    Optional<Integer> idAsInteger = id.asInteger();
    return idAsInteger.isPresent() && idAsInteger.get() >= BASE_ID;
}

【讨论】:

  • 甚至不需要多线程:对同一方法的两次调用不能保证返回相同的东西,而且我不确定 Sonar 是否能够保证 asInteger() 是幂等的。
  • @Andreas 很好,1+,但我会完全把它写成一个单一的声明
  • @JBNizet Sonar 永远不能保证不同类中方法的任何属性,因为它甚至不能保证在运行时实现是相同的。唯一的例外是具有精确定义的合同或以注释形式表示的正式合同的众所周知 (JDK) 方法。
【解决方案3】:

使用 Optional 时,您应该尽可能避免使用 .isPresent.get。使用这些方法并不比使用 null 更安全,而且违背了功能精神。可选项用于函数式编程和空检查的类型安全替代方案。

SonarQube 的分析功能有限。它通常不能排除各种误报。这种情况并不是真正的问题,因为不建议以这种方式使用 Optionals。

【讨论】:

  • 没有必要。例如,当你想在价值不存在时采取一些行动时,你如何不使用isPresent
  • @tsolakp 自 Java 9 yourOptional.ifPresentOrElse(dummy, this::someAction);.不过,您的观点是正确的,它是罕见(甚至可能是广受欢迎的?)案例之一,人们可能会为isPresent() 的使用辩护。
  • 是的,我的意图不是说“你不能使用 isPresent”,而是“在大多数情况下,如果可能的话最好避免使用它”。
【解决方案4】:

为了避免这个问题,我使用 iterator().next() 它具有与 .get 相同的功能,但它没有 isPresent() 问题!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-17
    • 1970-01-01
    • 2022-01-17
    • 2018-03-05
    • 2012-04-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多