我希望使我的代码更具可读性,并使用 IDE 代码检查和 / 或静态代码分析(FindBugs 和 Sonar)等工具来避免 NullPointerExceptions。许多工具似乎与彼此的@NotNull
/ @NonNull
/ @Nonnull
注释不兼容,并且在我的代码中列出所有这些工具都很难阅读。有什么建议是 “最好的” 吗?这是我发现的等效注释列表:
javax.validation.constraints.NotNull
创建用于运行时验证,而不是静态分析。
文件
edu.umd.cs.findbugs.annotations.NonNull
由Findbugs静态分析使用,因此声纳(现为Sonarqube )
文件
javax.annotation.Nonnull
这可能也适用于 Findbugs,但JSR-305处于非活动状态。 (另请参阅: JSR 305 的状态是什么? ) 来源
org.jetbrains.annotations.NotNull
由 IntelliJ IDEA IDE 用于静态分析。
文件
lombok.NonNull
用于控制Project Lombok 中的代码生成。
占位符注释,因为没有标准。
来源 , 文档
android.support.annotation.NonNull
Android 中提供的标记注释,由 support-annotations 包提供
文件
org.eclipse.jdt.annotation.NonNull
由 Eclipse 用于静态代码分析
文件
由于甲骨文目前决定不对 @NonNull(和 @Nullable)进行标准化 ,我担心没有好的答案。我们所能做的就是找到一个实用的解决方案,我的如下:
从纯粹的风格角度来看,我想避免任何对 IDE,框架或除 Java 本身之外的任何工具包的引用。
这排除了:
这给我们留下了 javax.validation.constraints 或 javax.annotation。前者配有 JEE。如果这比 javax.annotation 更好,这可能最终会出现在 JSE 中,或者从来没有出现过,这是一个有争议的问题。我个人更喜欢 javax.annotation,因为我不喜欢 JEE 依赖。
这让我们失望了
javax.annotation 中
这也是最短的一个。
只有一种语法更好:java.annotation.Nullable。由于过去其他软件包从 javax 毕业到 java,javax.annotation 将是朝着正确方向迈出的一步。
我希望它们都具有基本相同的简单实现,但详细的分析表明这不是真的。
首先是相似之处:
@NonNull 注释都有这一行
public @interface NonNull {}
除了
@Nullable 注释都有这一行
public @interface Nullable {}
除了(再次)org.jetbrains.annotations 及其简单的实现。
对于差异:
一个引人注目的是
都有运行时注释(@Retention(RUNTIME),而
只是编译时间(@Retention(CLASS))。
正如本回答中所述,运行时注释的影响比人们想象的要小,但除了编译时间之外,它们还有使工具能够进行运行时检查的好处。
另一个重要的区别是在代码可以使用注释。有两种不同的方法。某些包使用 JLS 9.6.4.1 样式上下文。下表给出了概述:
FIELD METHOD PARAMETER LOCAL_VARIABLE android.support.annotation X X X edu.umd.cs.findbugs.annotations X X X X org.jetbrains.annotation X X X X lombok X X X X javax.validation.constraints X X X
org.eclipse.jdt.annotation,javax.annotation 和 org.checkerframework.checker.nullness.qual 使用 JLS 4.11 中定义的上下文,我认为这是正确的方法。
这让我们失望了
在这一轮。
为了帮助您自己比较更多详细信息,我列出了下面每个注释的代码。为了便于比较,我删除了注释,导入和 @Documented 注释。 (他们都有 @Documented,但 Android 包中的类除外)。我重新排序了行和 @Target 字段并对资格进行了规范化。
package android.support.annotation;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER})
public @interface NonNull {}
package edu.umd.cs.findbugs.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NonNull {}
package org.eclipse.jdt.annotation;
@Retention(CLASS)
@Target({ TYPE_USE })
public @interface NonNull {}
package org.jetbrains.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NotNull {String value() default "";}
package javax.annotation;
@TypeQualifier
@Retention(RUNTIME)
public @interface Nonnull {
When when() default When.ALWAYS;
static class Checker implements TypeQualifierValidator<Nonnull> {
public When forConstantValue(Nonnull qualifierqualifierArgument,
Object value) {
if (value == null)
return When.NEVER;
return When.ALWAYS;
}
}
}
package org.checkerframework.checker.nullness.qual;
@Retention(RUNTIME)
@Target({TYPE_USE, TYPE_PARAMETER})
@SubtypeOf(MonotonicNonNull.class)
@ImplicitFor(
types = {
TypeKind.PACKAGE,
TypeKind.INT,
TypeKind.BOOLEAN,
TypeKind.CHAR,
TypeKind.DOUBLE,
TypeKind.FLOAT,
TypeKind.LONG,
TypeKind.SHORT,
TypeKind.BYTE
},
literals = {LiteralKind.STRING}
)
@DefaultQualifierInHierarchy
@DefaultFor({TypeUseLocation.EXCEPTION_PARAMETER})
@DefaultInUncheckedCodeFor({TypeUseLocation.PARAMETER, TypeUseLocation.LOWER_BOUND})
public @interface NonNull {}
为了完整性,这里是 @Nullable 实现:
package android.support.annotation;
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD})
public @interface Nullable {}
package edu.umd.cs.findbugs.annotations;
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
@Retention(CLASS)
public @interface Nullable {}
package org.eclipse.jdt.annotation;
@Retention(CLASS)
@Target({ TYPE_USE })
public @interface Nullable {}
package org.jetbrains.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface Nullable {String value() default "";}
package javax.annotation;
@TypeQualifierNickname
@Nonnull(when = When.UNKNOWN)
@Retention(RUNTIME)
public @interface Nullable {}
package org.checkerframework.checker.nullness.qual;
@Retention(RUNTIME)
@Target({TYPE_USE, TYPE_PARAMETER})
@SubtypeOf({})
@ImplicitFor(
literals = {LiteralKind.NULL},
typeNames = {java.lang.Void.class}
)
@DefaultInUncheckedCodeFor({TypeUseLocation.RETURN, TypeUseLocation.UPPER_BOUND})
public @interface Nullable {}
以下两个包没有 @Nullable,所以我单独列出它们 lombok 有一个非常无聊的 @NonNull。在 javax.validation.constraints 中,@ NonNull 实际上是 @NotNull,它有一个很长的实现。
package lombok;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NonNull {}
package javax.validation.constraints;
@Retention(RUNTIME)
@Target({ FIELD, METHOD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Constraint(validatedBy = {})
public @interface NotNull {
String message() default "{javax.validation.constraints.NotNull.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default {};
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@interface List {
NotNull[] value();
}
}
从我的经验来看,javax.annotation 至少得到 Eclipse 和 Checker Framework 的支持。
我理想的注释是带有 Checker Framework 实现的 java.annotation 语法。
如果您不打算使用 Checker Framework,那么javax.annotation ( JSR-305 )仍然是您最好的选择。
如果您愿意购买 Checker Framework,只需使用他们的 org.checkerframework.checker.nullness.qual。
我非常喜欢Checker Framework ,它是类型注释( JSR-308 )的实现,用于实现缺陷检查器之类的缺陷检查器。我还没有真正尝试任何其他人提供任何比较,但我对这个实现感到满意。
我不是提供该软件的团体的附属机构,但我是粉丝。
我喜欢这个系统的四件事:
它有一个缺陷跳棋NULL 的含量 (@Nullable),但也有药粥不变性和实习 (及其他)。我使用第一个(nullness),我正在尝试使用第二个(immutability / IGJ)。我正在尝试第三个,但我不确定长期使用它。我还不相信其他检查器的一般用处,但很高兴知道框架本身是一个用于实现各种附加注释和检查器的系统。
nullness 检查的默认设置很有效:除了本地(NNEL)之外的非 null。基本上这意味着默认情况下,检查器会处理除局部变量之外的每个(实例变量,方法参数,泛型类型等),就像它们默认具有 @NonNull 类型一样。根据文件:
NNEL 默认值会导致代码中显式注释的数量最少。
如果 NNEL 不适合您,您可以为类或方法设置不同的默认值。
通过将注释括在注释中,此框架允许您使用而不创建对框架的依赖 :例如/*@Nullable*/
。这很好,因为您可以注释和检查库或共享代码,但仍然能够在不使用框架的另一个项目中使用该库 / 共享编码。这是一个很好的功能。我已经习惯使用它,即使我现在倾向于在我的所有项目中启用 Checker Framework。
我使用 IntelliJ,因为我主要关注 IntelliJ 标记可能产生 NPE 的东西。我同意在 JDK 中没有标准注释令人沮丧。有关添加它的讨论,它可能会成为 Java 7. 在这种情况下,还有一个可供选择!