首页 > 解决方案 > 改进 Intellij 代码检查以防止可能产生 NullPointerException 警告

问题描述

我有一个类,它带有一个hasField检查字段是否存在且不为 null 的getField函数,以及一个返回字段值的函数(如果不存在,则返回 null)。

在我的代码中,当我getField在检查后立即调用时hasField,我知道 getField 不会返回 null,但 IDE 检查(常量条件和异常)不知道这一点。我得到一堆方法method name可能会产生一个NullPointerException

我正在尝试找到一种干净的方法来消除此警告。

解决方法

以下是我可以做的一些变通办法,但我发现所有这些都很奇怪:

  1. 包围getFieldObjects.requireNotnull代码将是无操作的。宁愿不这样做,因为它会使代码的可读性稍差。
  2. 在我知道这是安全的地方禁止警告。同样不是首选,因为这将发生在我们代码中的很多地方。
  3. 忽略警告。在这种情况下,我们可能会因为警告部分太吵而错过合法警告。

理想的解决方案

我是否能够以某种方式设置警告,如果hasField为真,那么getField将返回非空值?我查看了JetBrains Contract Annotations但在这里做我想做的似乎超出了@Contract 所支持的范围

代码示例

这是演示该问题的最低工作代码示例:

import javax.annotation.Nullable;

public class Hello {

  private Hello(){}
  public static void main(String[] args) {
    TestClass test1 = new TestClass(null);
    if (test1.hasSample()) {
      System.out.println(test1.getSample().equals("abc"));
    }
 }
}

class TestClass {
  private final String sample;

  TestClass(String field) { this.sample = field; }

  boolean hasSample() { return sample != null; }

  @Nullable public String getSample() { return sample; }
}

我收到以下警告

方法调用equals可能产生NullPointerException

理想情况下,当 hasSample 为真时,我希望能够告诉 IDE getSample 不为空。

标签: javaintellij-ideaannotationscode-inspection

解决方案


披露我是负责这个子系统的 IntelliJ IDEA 开发人员


不,现在不可能。假设您无法更改 API,没有比您已经列出的可能解决方法更好的解决方案了。我们拥有的最接近的东西是非常琐碎的方法的内联。但是,它仅在以下情况下有效:

  • hasSample()类似和的方法getSample()是从同一个类中调用的
  • 被调用的方法不能被覆盖(私有/静态/最终/在最终类中声明)

例如,此功能在以下代码中起作用:

final class TestClass { // if final is removed, the warning will appear again
  private final String sample;

  TestClass(String field) { this.sample = field; }

  boolean hasSample() { return sample != null; }

  @Nullable
  public String getSample() { return sample; }

  @Override
  public String toString() {
    if (hasSample()) {
      return "TestClass: "+getSample().trim(); // no warning on trim() invocation here
    }
    return "TestClass";
  }
}

目前,我只能建议将您的 API 重构为 Optionals,如下所示:

import java.util.Optional;

public class Hello {

  private Hello(){}
  public static void main(String[] args) {
    TestClass test1 = new TestClass(null);
    test1.getSample().ifPresent(s -> System.out.println(s.equals("abc")));
    // or fancier: test1.getSample().map("abc"::equals).ifPresent(System.out::println);
  }
}

final class TestClass {
  private final String sample;

  TestClass(String field) { this.sample = field; }

  public Optional<String> getSample() { return Optional.ofNullable(sample); }
}

推荐阅读