如何更有效地使用@Nullable和@Nonnull注释

我可以看到@Nullable@Nonnull注释有助于防止NullPointerException但是它们不会传播很远。

  • 这些注释的有效性在一层间接后完全消失,所以如果只添加一些注释,它们就不会传播很远。
  • 由于这些注释没有被很好地执行,所以存在假设用@Nonnull标记的值不为空并且因此不执行空检查的危险。

下面的代码会使用@Nonnull标记的参数为null而不会引发任何投诉。 它在运行时抛出一个NullPointerExceptionexception。

 public class Clazz { public static void main(String[] args){ Clazz clazz = new Clazz(); // this line raises a complaint with the IDE (IntelliJ 11) clazz.directPathToA(null); // this line does not clazz.indirectPathToA(null); } public void indirectPathToA(Integer y){ directPathToA(y); } public void directPathToA(@Nonnull Integer x){ x.toString(); // do stuff to x } } 

有没有办法使这些注释更加严格执行和/或进一步传播?

简短的回答:我猜这些注释只对你的IDE有用,提醒你潜在的空指针错误。

正如“清洁代码”一书所述,您应该检查公开方法的参数,并避免检查不variables。

另一个好的提示是永远不会返回空值,而是使用空对象模式 。

在你的IDE旁边,提示你在预期不为null地方传递null,你还有其他优点:

  • 静态代码分析工具可以testing相同的IDE(例如FindBugs)。
  • 你可以用AOP来检查这个断言。

所以它可以帮助创build更易维护的代码(不需要null检查)并且不太容易出错。

在符合1.8的Eclipse中编译原始示例,并且启用了基于注解的空分析,我们得到以下警告:

  directPathToA(y); ^ Null type safety (type annotations): The expression of type 'Integer' needs unchecked conversion to conform to '@NonNull Integer' 

这个警告类似于使用原始types(“unchecked conversion”)将遗传代码与遗留代码混合时得到的警告。 我们在这里有完全相同的情况:方法indirectPathToA()具有“遗留”签名,因为它没有指定任何空合约。 工具可以很容易地报告这一点,所以他们会追逐你所有的空白注释需要传播,但尚未。

而当使用聪明的@NonNullByDefault我们甚至不必每次都这样说。

换句话说,空注释是否“传播得很远”可能取决于您使用的工具以及您是否严格遵守工具发出的所有警告。 使用TYPE_USE空注释,您终于可以select让工具向您提供有关程序中每个可能的NPE的警告,因为nullness已成为types系统的一种内在属性。

我同意注释“不会传播很远”。 但是,我看到程序员方面的错误。

我将Nonnull注解理解为文档。 以下方法表示需要(作为先决条件)非空参数x

  public void directPathToA(@Nonnull Integer x){ x.toString(); // do stuff to x } 

下面的代码片段包含一个错误。 该方法调用directPathToA()而不强制y不为空(也就是说,它不保证被调用方法的先决条件)。 一种可能性是添加Nonnull注释以及indirectPathToA() (传播前提条件)。 可能性二是检查indirectPathToA()y的无效性,并避免在y为空时调用directPathToA()

  public void indirectPathToA(Integer y){ directPathToA(y); } 

我认为这个原始的问题间接指出了一个普遍的build议,即使使用了@NonNull,仍然需要运行时空指针检查。 请参阅以下链接:

Java 8新的Type Annotations

在上面的博客中,build议:

可选types注解不能替代运行时validation在“types注释”之前,用于描述可空性或范围的主要位置在javadoc中。 通过Type注释,这个通信以编译时validation的方式进入字节码。 你的代码仍然应该执行运行时validation。

我在项目中做的是在“恒定条件和例外”代码检查中激活以下选项:
为可能返回空值的方法build议@Nullable注释,并报告传递给未注释参数的可空值 检查

激活时,所有未注释的参数将被视为非空值,因此您也将在间接调用中看到警告:

 clazz.indirectPathToA(null); 

为了更强大的检查,Checker框架可能是一个不错的select(请参阅这个很好的教程 。
注意 :我还没有使用过,并且Jack编译器可能有问题:请参阅此bugreport

在Java中我会使用Guava的可选types 。 作为一个实际的types,你得到编译器保证它的使用。 很容易绕过它,并获得一个NullPointerException ,但至less该方法的签名清楚地传达了它所期望的作为参数或可能返回的内容。

如果您使用Kotlin,它将在编译器中支持这些可空性注释,并且会阻止您将空值传递给需要非空参数的java方法。 虽然这个问题的事件最初是针对Java的,但是我提到了这个Kotlin特性,因为它专门针对这些Java注释 ,问题是“是否有办法使这些注释更加严格地执行和/或进一步传播? 而这个特性确实使这些注解更加严格地执行

Java类使用@NotNull注释

 public class MyJavaClazz { public void foo(@NotNull String myString) { // will result in an NPE if myString is null myString.hashCode(); } } 

Kotlin类调用Java类并为用@NotNull注释的parameter passingnull

 class MyKotlinClazz { fun foo() { MyJavaClazz().foo(null) } } 

Kotlin编译器错误强制@NotNull注释。

 Error:(5, 27) Kotlin: Null can not be a value of a non-null type String 

请参阅: http : //kotlinlang.org/docs/reference/java-interop.html#nullability-annotations