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

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

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

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

 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有用,可能会出现空指针错误的警告。

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

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

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

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

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

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

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

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

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

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

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

我将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); } 

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

Java 8新的Type Annotations

在上面的博客中,建议:

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

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

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

 clazz.indirectPathToA(null); 

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

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

如果您使用Kotlin,它将在其编译器中支持这些可空性注释,并会阻止您将null传递给需要非空参数的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注释的参数传递null

 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

Interesting Posts