163

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

  • 这些注释的有效性在一级间接后完全下降,所以如果你只添加一些,它们不会传播很远。
  • 由于这些注释没有得到很好的执行,因此存在假设标记为 的值@Nonnull不是 null 并因此不执行 null 检查的危险。

下面的代码导致标记为的参数@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        
    }
}

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

4

9 回答 9

77

简短的回答:我猜这些注释仅对您的 IDE 有用,以警告您潜在的空指针错误。

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

另一个好技巧是永远不要返回空值,而是使用空对象模式

于 2012-11-21T01:34:42.220 回答
38

除了当您传递null给期望参数不为 null 的方法时,您的 IDE 会给您提示之外,还有其他优点:

  • 静态代码分析工具可以像你的 IDE 一样测试(例如 FindBugs)
  • 您可以使用面向方面的编程 (AOP)来检查这些断言

这可以帮助您的代码更易于维护(因为您不需要null检查)并且不易出错。

于 2012-11-21T08:57:28.723 回答
16

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

Java 8 的新类型注解

在上面的博客中,建议:

可选类型注释不能替代运行时验证 在类型注释之前,描述可空性或范围等内容的主要位置是在 javadoc 中。使用类型注释,这种通信以编译时验证的方式进入字节码。您的代码仍应执行运行时验证。

于 2016-06-22T16:37:58.530 回答
13

在 Eclipse 中以合规性 1.8 编译原始示例并启用基于注释的 null 分析,我们收到以下警告:

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

此警告的措辞类似于您在使用原始类型(“未经检查的转换”)将通用代码与遗留代码混合时收到的警告。我们在这里有完全相同的情况:方法indirectPathToA()有一个“遗留”签名,因为它没有指定任何空合约。工具可以轻松地报告这一点,因此它们会在需要传播但尚未传播空注释的所有小巷中追逐你。

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

换句话说:空注释是否“传播得很远”可能取决于您使用的工具,以及您对工具发出的所有警告的关注程度。使用TYPE_USE 空注释,您最终可以选择让工具警告您程序中每个可能的 NPE,因为空性已成为类型系统的固有属性。

于 2015-09-18T22:06:11.820 回答
8

我在我的项目中所做的是在“常量条件和异常”代码检查中激活以下选项:
为可能返回 null 并报告传递给非注释参数的可空值的方法建议 @Nullable 注释 检查

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

clazz.indirectPathToA(null); 

对于更强大的检查,Checker Framework 可能是一个不错的选择(请参阅这个不错的教程
注意:我还没有使用过,Jack 编译器可能存在问题:请参阅此错误报告

于 2017-01-16T15:25:45.083 回答
7

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

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

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

然后以下代码片段包含一个错误。方法调用directPathToA()而不强制y非空(即,它不保证被调用方法的前提条件)。一种可能性是将Nonnull注释也添加到indirectPathToA()(传播前提条件)。可能性二是检查yin是否为空,indirectPathToA()避免调用directPathToA()wheny为空。

    public void indirectPathToA(Integer y){
        directPathToA(y);
    }
于 2015-09-01T06:33:24.563 回答
6

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

@NotNull使用注解的 Java 类

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)
    }
}  

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

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

于 2017-09-19T01:52:01.440 回答
3

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

于 2012-11-21T01:38:08.170 回答
-4

由于 Java 8 的新特性Optional你不应该在你自己的代码中使用 @Nullable 或 @Notnull 了。举个例子:

public void printValue(@Nullable myValue) {
    if (myValue != null) {
        System.out.print(myValue);
    } else {
        System.out.print("I dont have a value");
}

它可以重写为:

public void printValue(Optional<String> myValue) {
    if (myValue.ifPresent) {
        System.out.print(myValue.get());
    } else {
        System.out.print("I dont have a value");
}

使用可选项强制您检查空值。在上面的代码中,您只能通过调用get方法来访问值。

另一个优点是代码变得更具可读性。随着 Java 9 ifPresentOrElse的加入,该函数甚至可以写成:

public void printValue(Optional<String> myValue) {
    myValue.ifPresentOrElse(
        v -> System.out.print(v),
        () -> System.out.print("I dont have a value"),
    )
}
于 2019-12-20T17:59:15.250 回答