我应该使用哪个NotNull Java注释?

我应该使用哪个@NotNull Java注释?| Java Debug 笔记

本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看<活动链接>

提问:

我希望使我的代码更具可读性,并使用诸如IDE代码检查和 / 或静态代码分析(FindBugs和Sonar——这两个是检查代码规范的工具集,译者注)之类的工具来避免NullPointerExceptions。许多工具似乎彼此互不兼容,在我的代码中列出了这些工具里的@NotNull / @NonNull / @Nonnull注释,所有这些都很难读。你们有没有最佳的建议?这是我发现的等效注释的列表:

  • javax.validation.constraints.NotNull
    创建用于运行时验证,而非静态分析。
    注释文档
  • edu.umd.cs.findbugs.annotations.NonNull
    FindBugs (dead project) 和他的继承类 SpotBugs 静态分析和 Sonar (现在叫 Sonarqube)使用

FindBugs 注释文档, SpotBugs 注释文档

  • javax.annotation.Nonnull
    这可能也适用于FindBugs, 但是JSR-305 处于非活动状态(不太被很多人使用的意思*,*译者注)。 (也可以看如下资料: What is the status of 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用于静态代码分析
    注释文档

回答:

由于JSR 305(其目标是使@NonNull@Nullable标准化)已经休眠了几年,所以恐怕没有好的答案。我们所能做的就是找到一个务实的解决方案,我的方法如下:

句法

从纯粹的风格角度来看,除了Java本身,我想避免引用任何IDE,框架或任何工具包。

这排除了:

  • android.support.annotation
  • edu.umd.cs.findbugs.annotations
  • org.eclipse.jdt.annotation
  • org.jetbrains.annotations
  • org.checkerframework.checker.nullness.qual
  • lombok.NonNull

剩下的就是javax.validation.constraintsjavax.annotation。前者带有JEE。如果这比javax.annotation更好,它最终可能会与JSE一起出现,或者根本不会出现,那是有争议的问题。我个人更喜欢javax.annotation,因为我不喜欢JEE依赖项。

这给我们留下了

javax.annotation

这也是最短的。

只有一种语法会更好:java.annotation.Nullable。随着过去其他软件包从javax升级到java,javax.annotation将是朝着正确方向迈出的一步。

执行

我希望它们都具有基本相同的琐碎实现,但是经过详细的分析表明,事实并非如此。

首先是相似之处:

@NonNull批注都有一行:

1
java复制代码public @interface NonNull {}

除了:

  • org.jetbrains.annotations将其称为@NotNull并具有简单的实现
  • 具有更长实现的javax.annotation
  • javax.validation.constraints也称为@NotNull并具有实现

@Nullableannotations都有一行

1
java复制代码public @interface Nullable{}

再次除了org.jetbrains.annotations以及他们的一些实现。

对于差异:

引人注目的是

  • javax.annotation
  • javax.validation.constraints
  • org.checkerframework.checker.nullness.qual

都有运行时注释 (@Retention(RUNTIME))。而

  • android.support.annotation
  • edu.umd.cs.findbugs.annotations
  • org.eclipse.jdt.annotation
  • org.jetbrains.annotations

只是编译阶段才有 (@Retention(CLASS))。

此回答所述,运行时批注的影响比人们想象的要小,但是它们的好处是,除了编译时,它还使工具能够执行运行时检查。

另一个重要的区别是注释可以在代码中的何处使用。有两种不同的方法。一些软件包使用JLS 9.6.4.1样式上下文。下表概述了:

1
2
3
4
5
6
kotlin复制代码                                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.annotationorg.checkerframework.checker.nullness.qua使用JLS 4.11中定义的上下文,我认为这是正确的方法。

这给我们留下了

  • javax.annotation
  • org.checkerframework.checker.nullness.qual

代码示例

为了帮助您自己比较更多详细信息,我在下面列出了每个注释的代码。为了使比较容易,我删除了注释,导入和@Documented批注。 (除了Android包中的类之外,其他所有文件都具有@Documented)。我对这些代码行和@Target字段进行了重新排序,并进行了规范化。

1
2
3
4
java复制代码package android.support.annotation;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER})
public @interface NonNull {}

1
2
3
4
java复制代码package edu.umd.cs.findbugs.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NonNull {}

1
2
3
4
java复制代码package org.eclipse.jdt.annotation;
@Retention(CLASS)
@Target({ TYPE_USE })
public @interface NonNull {}

1
2
3
4
java复制代码package org.jetbrains.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NotNull {String value() default "";}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码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;
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
java复制代码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实现:

1
2
3
4
java复制代码package android.support.annotation;
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD})
public @interface Nullable {}

1
2
3
4
java复制代码package edu.umd.cs.findbugs.annotations;
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
@Retention(CLASS)
public @interface Nullable {}

1
2
3
4
java复制代码package org.eclipse.jdt.annotation;
@Retention(CLASS)
@Target({ TYPE_USE })
public @interface Nullable {}

1
2
3
4
java复制代码package org.jetbrains.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface Nullable {String value() default "";}

1
2
3
4
5
java复制代码package javax.annotation;
@TypeQualifierNickname
@Nonnull(when = When.UNKNOWN)
@Retention(RUNTIME)
public @interface Nullable {}

1
2
3
4
5
6
7
8
9
10
java复制代码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,因此我将它们分开列出;Lombox有一个很少用的@NonNul。在javax.validation.constraints中,@NonNull实际上是一个@NotNull,并且执行耗时很长。

1
2
3
4
java复制代码package lombok;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NonNull {}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java复制代码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();
}
}

支持

根据我的经验,Eclipse和Checker Framework至少是开箱即用地支持javax.annotation

总结

我理想的注释是Checker Framework实现的java.annotation语法。

如果您不打算使用Checker Framework,则暂时最好还是使用javax.annotationJSR-305)。

如果您愿意尝试Checker Framework,请使用其org.checkerframework.checker.nullness.qual

用到的源码

  • android.support.annotation from android-5.1.1_r1.jar
  • edu.umd.cs.findbugs.annotations from findbugs-annotations-1.0.0.jar
  • org.eclipse.jdt.annotation from org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
  • org.jetbrains.annotations from jetbrains-annotations-13.0.jar
  • javax.annotation from gwt-dev-2.5.1-sources.jar
  • org.checkerframework.checker.nullness.qual from checker-framework-2.1.9.zip
  • lombok from lombok commit f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints from validation-api-1.0.0.GA-sources.jar

原文链接

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%