如何在getter链中跟踪NullPointerException

Len*_*mel 26 java debugging getter nullpointerexception

如果我在这样的调用中得到NullPointerException:

someObject.getSomething().getSomethingElse().
    getAnotherThing().getYetAnotherObject().getValue();
Run Code Online (Sandbox Code Playgroud)

我得到一个相当无用的异常文本,如:

Exception in thread "main" java.lang.NullPointerException
at package.SomeClass.someMethod(SomeClass.java:12)
Run Code Online (Sandbox Code Playgroud)

我发现很难找到实际上调用returend null,经常发现自己将代码重构为这样的东西:

Foo ret1 = someObject.getSomething();
Bar ret2 = ret1.getSomethingElse();
Baz ret3 = ret2.getAnotherThing();
Bam ret4 = ret3.getYetAnotherOject();
int ret5 = ret4.getValue();
Run Code Online (Sandbox Code Playgroud)

然后等待更具描述性的NullPointerException,告诉我要查找哪一行.

你们中的一些人可能认为连接getter是一种糟糕的风格,无论如何都应该避免,但我的问题是:我可以在不更改代码的情况下找到错误吗?

提示:我正在使用eclipse,我知道调试器是什么,但我无法弄清楚如何将它应用于问题.

我对答案的结论是:
有些答案告诉我,我不应该一个接一个地连接吸气剂,一些答案显示我如何调试我的代码,如果我不赞成这个建议.

我已经接受了一个答案,这个答案让我知道什么时候连接吸气剂:

  • 如果他们不能返回null,只要你喜欢就链接它们.不需要检查!= null,无需担心NullPointerExceptions(请注意链接仍然是demeter的法则,但我可以忍受)
  • 如果它们可能返回null,则不要永远不会链接它们,并对每个可能返回null的值执行空值检查

这使得对实际调试的任何好建议毫无用处.

Esk*_*sko 12

NPE是Java中最无用的Exception期间.它似乎总是懒惰地实现,并且永远不会确切地告诉它是什么导致它,即使像"类xyZ为空"这样简单也会在调试这种情况时提供很多帮助.

无论如何,我发现在这些情况下找到NPE投掷者的唯一好方法是以下类型的重构:

someObject.getSomething()
          .getSomethingElse()
          .getAnotherThing()
          .getYetAnotherObject()
          .getValue();
Run Code Online (Sandbox Code Playgroud)

你有它,现在NPE指向正确的线,因此正确的方法抛出实际的NPE.不像我想要的那样优雅的解决方案,但它的工作原理.

  • 自从我在 8 年前发布这篇文章以来,我就注意到我所声称的并不总是正确的。我怀疑它与 Java 7 和 8 中的字节码更改有关,但实际上我懒得确认。 (2认同)

Ron*_*hke 9

答案取决于你如何看待你的吸气者(合同).如果他们可能会返回,null您应该每次都检查返回值.如果getter不应该返回null,getter应该包含一个检查并抛出异常(IllegalStateException?)而不是返回null,你承诺永远不会返回.堆栈跟踪将指向您确切的getter.你甚至可以在异常消息中找到意外状态你的getter.


Mo.*_*Mo. 9

在IntelliJ IDEA中,您可以设置exceptionbreakpoints.抛出指定的异常时会触发这些断点(您可以将其范围限定为包或类).

这样就很容易找到NPE的来源.

我认为,你可以在netbeans或eclipse中做类似的事情.

编辑:是一个关于如何在eclipse中添加exceptionbreakpoint的解释


Ros*_*oss 6

如果你发现自己经常写作:

a.getB().getC().getD().getE();
Run Code Online (Sandbox Code Playgroud)

这可能是代码味道,应该避免.例如,您可以重构a.getE()哪些调用b.getE()调用c.getE()哪些调用d.getE().(此示例可能对您的特定用例没有意义,但它是修复此代码气味的一种模式.)

另见德米特定律,其中说:

  • 您的方法可以直接调用其类中的其他方法
  • 您的方法可以直接在自己的字段上调用方法(但不能在字段的字段上)
  • 当您的方法接受参数时,您的方法可以直接调用这些参数的方法.
  • 当您的方法创建本地对象时,该方法可以调用本地对象上的方法.

因此,不应该有一个消息链,例如a.getB().getC().doSomething().遵循这个"法律"除了使NullPointerExceptions更容易调试之外还有许多其他好处.