【问题标题】:How to get warned about NPE caused by functions returning null?如何获得有关由返回 null 的函数引起的 NPE 的警告?
【发布时间】:2014-02-07 10:26:50
【问题描述】:

背景

有时,函数在某些情况下返回 null,但使用它们的人并没有意识到这一点,因此 NPE 是不可避免的。

例如(它只是一个示例,以说明我在说什么):

public class Test
  {
  public static void main(final String[] args)
    {
    final int t=0;
    System.out.println("result:"+foo(t).toString()); // no warning here...
    }

  public static String foo(final int t)
    {
    if(t>0)
      return "positive";
    else return null;
    }
  }

问题

Eclipse 不会警告这样的事情,甚至在设置中也没有。

只有在不涉及任何功能时才能检测到此类问题(请参阅下面的我的编辑)。

不仅如此,我还找不到说明返回值可以为空的注释。我认为它有相反的效果。

我尝试过的

所以我认为由于 Eclipse 没有一些检查(这很好),我可以使用替代的静态代码分析工具,例如 FindBugsCodePro Analytix 。两者都未能找到此类错误。

我已尝试联系这两个项目,但尚未得到任何答复。对于 CodePro,我什至不确定我是否做对了……事实上,我认为这个项目已经停止(最后一次更新是 2010 年……)。

问题

是否有任何工具可以帮助避免此类错误?


编辑:由于许多人认为这可以通过更改代码来解决,因此请考虑在一个庞大的团队中工作,而有些人可能已经忘记了它(每个人都可能犯错误)。或者,您甚至可能正在开发一个使用 SDK(甚至可能是封闭源代码)的项目,该 SDK 在某些情况下可能返回 null,但由于 SDK 的创建者忘记了它,因此没有记录在案。

无论你是一名多么优秀的程序员,这种事情都可能发生。

我要求的是通过让 eclipse 帮助来克服这个问题的方法。它应该能够警告我,至少在我在这里写的基本示例上。

Eclipse 应该能够进行这样的检查,就像它可以警告我这个例子一样:

int t=0;
--t;
String s="s";
if(t<0)
  s=null;
System.out.println(s.toString()); // here you will get a warning

当你在 Eclipse 的设置中启用它时可以检测到:

Java-> Compiler-> Errors/Warning-> Potential null pointer access

我喜欢 Eclipse,因为它有很多很酷的警告和错误,可以帮助避免错误,但我上面写的内容无法被它检测到,甚至无法被我谈到的工具检测到。

许多错误很难找到,没有人是不犯错误的完美程序员。即使您确实知道很多,您也不会受到其他人的错误的保护。人们甚至可能无法找到基本代码的问题(请参阅herehere 或只是here,看看你自己能找到多少错误)。这就是为什么有一些工具可以帮助您。

【问题讨论】:

  • 首先,您应该考虑返回null 是否是一种好习惯。 :)
  • 考虑使用Optional 样式结构来表示意图;有one in Guava
  • @Gimby 即使我添加了 javadocs,它也不会强迫人们阅读它。此外,我也可能会因为忘记添加 JavaDocs 而犯错,甚至可能使用没有说明获取 null 值的可能性的 SDK。我试图表明我所写的只是一个示例,但请考虑获得这样一个东西的所有可能选择 - 无论你作为一名程序员有多优秀,它都可能发生。
  • @McDowell 您并不总是有机会编写您正在使用的代码。有时您使用 SDK 或在一个庞大的团队中工作。
  • FWIW Netbeans 也是如此 - 方法没有警告,但内联时会发出警告。

标签: java eclipse nullpointerexception eclipse-jdt static-code-analysis


【解决方案1】:

考虑在您的代码中使用@Nullable@NonNull 注释。它可以部分解决您的问题。

更新:从http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Ftasks%2Ftask-using_null_annotations.htm复制的使用示例

@NonNull String getString(String maybeString) {
    if (maybeString != null)
        return maybeString;                         // the above null check is required
    else
        return "<n/a>";
}
void caller(String s) {
    System.out.println(getString(s).toUpperCase()); // no null check required
}

更新:@Nullable 的情况

public @Nullable String getMessage() {
    return System.currentTimeMillis() % 2 != 0
        ? "That's odd..."
        : null;
}

public void showCase() {
    String msg = getMessage();
    if (msg.isEmpty()) { //- Here it yields about possible null access
        //...
    }
}

更新:我的环境中的空分析设置:

来自 Eclipse 的实际错误消息:

更新:您需要在构建路径中包含带有 Eclipse 注释的 JAR。将鼠标指针移动到无法编译的@NonNull@Nullable 注释上(如图所示),然后选择使用默认空注释复制库来构建路径

新的 JAR 应该出现在 Package Explorer 中并且编译器应该能够看到它。然后将光标移动到注释并按 Ctrl+Space — Eclipse 应该找到正确的导入(注意,您可能会在列表中看到两个不同的包 — 从 JDT 中选择一个) — 并选择它。它应该可以工作。

【讨论】:

  • 你能举个例子吗?另外,如果我在一个巨大的项目中工作,有很多人(其中一个人可能忘记添加它),或者当我处理没有说明获得可能性的 SDK 时,我该如何处理它空?
  • 请在此处阅读更多内容help.eclipse.org/juno/… 但正如我所说,它只能部分解决您的问题。如果你提到它很难广泛使用。您还可以在您最喜欢的 IDE 中启用更积极的 null 检查,以至少看到一些警告。
  • 你确定它可以应用于函数的返回值吗?我在您提供的链接中找不到它。就像我读过的其他链接一样,只有相反的注释,并且(我认为)仅适用于函数的参数。此外,即使有注释,也不能解决问题,因为您可能使用了不是您亲自编写的代码。
  • 好吧,它确实让您可以将其添加到构建路径中,只是不能从导入建议对话框中添加 --- 为什么要这样做?如果一个人不想使用它或有自己的注释,那么为什么要显示它呢?如果您检查构建路径设置并查看库选项卡,您将在那里看到一个额外的 JAR。无论如何它是好的它终于工作了。 8)
  • 这个奇怪的弹出窗口就是所谓的Quick Fix。它不会将 Java 包导入到您的源代码中,它只是将指定的 JAR 添加到您的构建路径中,因此它与 Java 导入建议完全不同。 Java 导入只能建议您构建路径中的这些条目。偶然有一个来自 com.sun.* 的注释具有相同的名称(我猜是 Nullable,或者它是 NonNull 还是 NotNull?)并且因为它在您的构建路径上,因为这是 JRE 的一部分,所以它只是在导入中显示建议。这并不奇怪 --- 实际上它的工作原理完全符合预期。
【解决方案2】:

为您的代码编写单元测试:

@Test
public void testFooWithZero() {
    try{
        Test tester = new Test();
        assertEquals("Expect string returned", "positive", tester.foo(0));
    catch(NullPointerException npe) {
        fail("Threw NPE");
    }
}

它们不仅描述了您的预期行为(通常比懒惰编写的 Javadoc 更好),还保证您的代码符合您的预期。

为正面和负面场景编写测试,你就很成功了。

静态代码分析当然有它的好处,但也有它的局限性——常见的情况是如何防止 get(Object) 从 HashMap 或类似集合返回 null。 SCA 无法在运行时知道集合中有哪些数据。您也无法对核心 Java 包或第三方的代码进行注释。

单元测试应该以互补的方式使用,并且会提高代码的质量。请参阅此问题,了解您应该考虑它的几个好处和原因:

Is Unit Testing worth the effort?

【讨论】:

  • 虽然在某些情况下这样做可能是件好事,但在任何项目(以及您可能使用的 SDK)的所有功能上都这样做是不切实际的......
  • 如果你对所有公共访问者都这样做,你应该实现非常高的覆盖率。您的问题是人们使用单元测试的原因 - 静态代码分析可能会捕获简单的错误,但永远不会检测您的应用程序是否按预期运行。单元测试可以。
  • 再一次,单元测试很棒,但为您、您的同事和第三方 SDK 开发人员制作的每个功能都制作一个是不切实际的。实在是太多了。您不妨自己创建一个工具来警告您此类情况......
  • “null”问题更为根本,无法通过单元测试可靠地避免。话虽这么说,但它通常是唯一的可能性,因此至少可以获得一些信心。
  • @Mikaveli 关于单元测试的重要性,我同意你的看法。我想我会假设您在这里以非静态方式调用 foo(),因为您在代码中反射性地避免使用静态方法,这也是一个好习惯。
【解决方案3】:

方法有两种方式来表示这种“失败”。返回 null 或抛出异常,这通常在 javadoc 中指示。返回 null 本身并不是错误,因为例如 HashMap.get(key) 将返回 null,如果没有与键关联的值。让HashMap 抛出异常将是一个糟糕的选择。另一方面,如果没有找到实体(例如,请求一个预期存在的唯一实体),一些 JPA 方法会抛出异常。

想象一下,如果您每次使用 HashMap,eclipse 都会说“小心,它可能会返回 null”,您会收到多少警告。

所以总结一下:在现实生活中警告它不会很有用,因为你会因为混乱而开始忽略警告。

【讨论】:

  • 你错过了我的意思。如果您在返回值上调用函数,并且在某些情况下它可能为 null,您应该得到一个警告。不管你是如何得到这个值的。 Eclipse 甚至可以以基本的方式检查代码,并在这种情况下发出警告。
猜你喜欢
  • 2011-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-23
  • 2020-10-03
相关资源
最近更新 更多