为什么Idea lint警告缺少`isPresent()`检入`orElseGet`?

时间:2017-06-20 19:56:02

标签: java java-8 optional

假设我不知道一个Optional是否为空,或两者是否存在。在后一种情况下,我总是希望a优先于b

final Optional<String> a = Optional.of("1");
final Optional<String> b = Optional.empty();
if (a.isPresent() || b.isPresent()) {
  // prefer a over b
  Integer result = a
      .map(s -> s + "0")
      .map(Integer::parseInt)
      .orElseGet(() -> Integer.parseInt(b.get())); // <-- warning for b.get()
  System.out.println(result);
}

在我的真实代码中,Idea在此时警告我:

  

'Optional.get()'没有'isPresent()'检查。

为什么?如果ab存在,我之前会检查。此外,此代码按预期工作,输出为10。如果我放b = Optional.of("2"),则输出仍为10,因为我更喜欢a。如果我然后放a = Optional.empty(),则输出结果为2

我做错了什么,或者是Idea的错误?

4 个答案:

答案 0 :(得分:4)

在这种情况下,看起来像IDEAs linting是错误的,但它很可能与决定b.isPresent()后备中a.orElseGet(...)是否得到保证的复杂性有关。 。

一般来说,我发现如果IDE警告某事,通常一个好主意(双关语)来修复它,因为下一个开发人员来看看代码也会很难解读代码背后的意图。

由于您已经在使用Optional,我会考虑根本不使用isPresent方法,并使用api为您完成工作。我可以看到两个应该满足警告的选项(双关语)。

如果两者都不存在,您可以提供合适的后备:

final Optional<String> a = Optional.of("1");
final Optional<String> b = Optional.empty();

// prefer a over b
Integer result = a
    .map(s -> s + "0")
    .map(Integer::parseInt)
    .orElseGet(() -> b.map(Integer::parseInt).orElse(0));

System.out.println(result);

如果你从不期望两者都是空的,你可以抛出IllegalStateException而不是后备:

final Optional<String> a = Optional.of("1");
final Optional<String> b = Optional.empty();

// prefer a over b
Integer result = a
    .map(s -> s + "0")
    .map(Integer::parseInt)
    .orElseGet(() -> b.map(Integer::parseInt).orElseThrow(IllegalStateException::new));

System.out.println(result);

答案 1 :(得分:3)

这个想法在这里很混乱,它的内部规则可能会假设对相同可选链接中的isPresent进行检查。

由于Optional,这显然位于(a.isPresent() || b.isPresent())检查之外;由于这些检查,无法判断无法在空的可选orElseGet上调用b ...

答案 2 :(得分:2)

长话短说:在IDEA 2017.3中修复(见IDEA-174759)。

是的,保证b出现在b.get()点。是的,IDEA警告是错误的。原因是当前版本中的IDEA分析不够复杂,无法理解可选链中的内容。请注意,结果还取决于Integer::parseInt方法引用永远不会生成null的事实。否则Optional.map即使a存在,也可能产生空可选项。例如,考虑以下代码:

private static Integer myParseInt(String s) {
    try {
        return Integer.parseInt(s);
    } catch (NumberFormatException e) {
        return null;
    }
}

...

if (a.isPresent() || b.isPresent()) {
    // prefer a over b
    Integer result = a.map(s -> s + "0").map(MyClass::myParseInt)
                      .orElseGet(() -> Integer.parseInt(b.get()));
    System.out.println(result);
}

如果b包含非数字字符串,则a可能不存在,因此需要提示警告。

我们不断改进IDEA静态分析。在2017.3中,它将理解这些结构:

IDEA 2017.3

正如您所看到的,现在警告未显示在原始代码中,但显示为可为空myParseInt

IDEA 2017.3尚未推出。但是,由于此功能是IDEA社区的一部分,您可以使用主分支中的GitHub sources自行构建。

免责声明:我是IntelliJ IDEA开发人员,我正在努力解决此问题。

答案 3 :(得分:0)

您正在检查a.isPresent()和b.isPresent()in或condition。你正在收到警告,因为可能会发生条件的第一部分证明是真的&#34; a.isPresent()&#34;但是b.isPresent()是假的。但是,由于||条件块内的代码将开始执行。在这种情况下,b.get将失败,并且#34; NoSuchElementException&#34;。

虽然根据你的逻辑,如果b不存在或空,b.get()是不可达的,因为你更喜欢超过b。但IDE会分析并主动告诉您所有错误或代码中的错误做法。

尝试做类似的事情:

if(a.isPresent()){
//logic if a is there
}
else if(b.isPresent){
//logic if a not there and b is there
}
else{
logic if both are not there
}