Java – 在不允许使用@NotNull或@Nullable注释的情况下,在编译时检测NPE的最佳方法
我曾经使用大量的@NotNull/@Nullable
注释来使IDE在编译时帮助我找出潜在的NPE
。 但是,我的新团队不允许使用@NotNull/@Nullable
注释,也不允许使用那些允许的自定义注释。 因此,我比以前更有可能写出NPE
导致的错误。
我尝试了几个解决方案:
- 在Java 8中使用
Optional<T>
。但是,这不是每个案件都很好。 通常不建议使用Optional<T>
作为字段或参数的类型。Optional<T>
实例本身可能为null
也是非常令人沮丧的。 另外,调用ifPresent(obj->...)
时,很难在lambda表达式中操作控制流(在Java 9中更容易)。 而使用太多的Optional<T>
使代码有点冗长。 (更新:不幸的是,Optional<T>
也被禁止使用) - 使IDE将每个未注释的实例视为
@Nullable
。 这个解决方案确实帮我找到了一些潜在的错误,但是IDE会建议我检查几乎所有的方法调用,这真的很烦人,因为许多方法都是故意不返回null
。 - 检查每个方法调用。 这是一个可行的解决方案,但是它具有严重的影响,即通过方法调用可能导致
null
。 在这种情况下,一个方法的每个参数都可能为null
,并且当一个参数被检查为无效时,该方法通常会连续返回null
。 最后,每个方法都被“感染”,并有可能收到null
参数并返回null
。 - 调用
Objects.requireNonNull()
来防止上述问题。 它稍微减少了无处不在的检查的痛苦。 但是,它不能保证当不允许null
的情况下,调用者不会向null
传递null
。 一旦null
传递,抛出的运行时NPE
更有可能破坏你的应用程序。 - 切换到kotlin。 当然,这是不允许的:)
是否有其他建议在编译时检测NPE(并保存我的工作)? 我认为这个问题的解决方案可以被广泛使用,不仅仅是帮助自己,因为并不是所有的团队都允许使用@NotNull/@Nullable
注解和Optional<T>
。
不是一个确定的答案,而是一些探索的方法:
- 像findBug , PMD , Coverity这样的工具在这个练习中是相当不错的。
- 检查你不能在团队中使用注释的原因。 也许你可以让你的团队改变主意? (也可能有一个很好的理由。)
- Eclipse IDE在这个练习中是非常好的。 您是否尝试了“Windows>首选项> Java>编译器>错误/警告>空分析”选项?
- 单元测试。 在一些测试案例中涵盖“不应该发生”的情况。 代码覆盖工具(如JaCoCo )也可能是有用的。
- 防御性编程(普通的'如果'语句,断言…)
- 以前的项目的混合
希望这可以帮助。
你没有说你正在使用什么IDE,但Eclipse和Intellij支持外部注释。 有了它们,您仍然可以在本地注释您的代码,并让IDE提供空分析。
Intellij文档
Eclipse文档
你也许可以找到一种方法来使用适合你的情况的Optional<T>
。
假设你必须写一个方法U doStuff(T param)
,并且这个param
是由其他人的一个库给你的代码,它可能是空的。
在doStuff
方法的内部,你可以做
return Optional.ofNullable(param) .map(notNullParam -> doThing(notNullParam)) .orElse(null);
通过这种方式,您可以确定您的Optional
本身不是空的,人们仍然可以使用您的API参数为null,并且在它们有意义时返回null。
我建议查看SonarLint ,这是一个非常好的工具,可以检测所有类型的错误和错误,包括NPE 。
你应该做什么 – 提出一个好的API设计,并指定哪些图层允许返回null,哪些不允许。
一般来说,你应该总是尝试返回其他的东西,而不是null – 空集合,一个代表not-found
的对象。
所以你应该手动检查空值,返回值可能是空的。 否则,只召集不返回null,只是不检查。
您还可以引入一些实用程序方法来检查空/空集合:
如果collection为null或者不为null,则Check.isEmpty()
将返回true,等等…
您也可以使用空对象模式
我可以把你的问题和我的用例联系起来。 在这些情况下,随意提出代码覆盖的工具。
- JaCoCo非常方便。 如果你使用IntelliJ,内置的Jacoco / IJ的代码覆盖率明确地给你违规和可能的NPE
- 单元测试,以支持您的域明智的非空对象的情况
毕竟, NPE仍然是基于设计/程序员的错误 。 保持您的代码经过充分测试和适当的检查,以避免NPE并抛出自定义异常或IllegalArgumentException
如果您的服务需要一个有效的值。