我希望使我的代码更具可读性,以及使用工具,如IDE代码检查和/或静态代码分析(FindBugs和Sonar),以避免nullpointerexception。许多工具似乎与彼此的@NotNull/@NonNull/ @NonNull注释不兼容,在我的代码中列出所有这些工具会让人难以阅读。有没有什么建议,哪一个是“最好的”?下面是我找到的等价注释列表:

javax.validation.constraints.NotNull Created for runtime validation, not static analysis. documentation edu.umd.cs.findbugs.annotations.NonNull Used by FindBugs (dead project) and its successor SpotBugs static analysis and therefore Sonar (now Sonarqube) FindBugs documentation, SpotBugs documentation javax.annotation.Nonnull This might work with FindBugs too, but JSR-305 is inactive. (See also: What is the status of JSR 305?) source org.jetbrains.annotations.NotNull Used by IntelliJ IDEA IDE for static analysis. documentation lombok.NonNull Used to control code generation in Project Lombok. Placeholder annotation since there is no standard. source, documentation androidx.annotation.NonNull Marker annotation available in Android, provided by annotation package documentation org.eclipse.jdt.annotation.NonNull Used by Eclipse for static code analysis documentation


当前回答

JSpecify将是一种方法(当它准备好时)。事实上,他们的陈述积极地与这个问题联系在一起,并明确表示他们的目标是最终得到一个好的答案。

它拥有Android、Guava和Kotlin等主要参与者。

其他回答

不幸的是,JSR 308将不会添加比这个项目本地非空建议更多的值

Java 8将不会提供单一的默认注释或自己的Checker框架。 类似于Find-bugs或JSR 305,这个JSR由一小群主要是学术团队维护得很差。

它背后没有商业力量,因此JSR 308现在发布EDR 3 (JCP的早期草案审查),而Java 8应该在不到6个月内发布:-O 顺便说一句,类似于310。但与308不同的是,Oracle现在已经从其创始人手中接管了这一业务,以最大限度地减少它对Java平台的伤害。

每个项目、供应商和学术类,比如检查器框架和JSR 308背后的那些,都将创建自己的专有检查器注释。

让源代码在未来几年不兼容,直到找到一些流行的妥协,并可能添加到Java 9或10中,或者通过Apache Commons或谷歌Guava这样的框架;-)

较新的项目可能应该使用jakarta。annotation-api(雅加达。注释包)。 它从现在的只读javax链接。注释回购和适应新的Jakarta生态系统,旨在将社区从所有与javax相关的头痛中解放出来。

只是指出Java验证API (javax.validation.constraints.*)没有提供@Nullable注释,这在静态分析上下文中非常有价值。这对于运行时bean验证是有意义的,因为这是Java中任何非基本字段的默认值(即没有需要验证/强制的内容)。为达到上述目的,应权衡备选方案。

安卓

这个答案是Android特有的。Android有一个支持包叫做support-annotations。它提供了几十个Android特定的注释,也提供了常见的注释,如NonNull, Nullable等。

要添加support-annotations包,在build.gradle中添加以下依赖项:

compile 'com.android.support:support-annotations:23.1.1'

然后使用:

import android.support.annotation.NonNull;

void foobar(@NonNull Foo bar) {}

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

语法

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

这就排除了:

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

这使得我们只能使用javax.validation.constraints或javax.annotation。 前者随JEE一起提供。如果这比javax更好。注释可能最终随JSE一起出现,也可能永远不会出现,这是一个有争议的问题。 我个人更喜欢javax。注释,因为我不喜欢JEE依赖。

这就留给我们

javax.annotation

这也是最短的一个。

只有一种语法会更好:java.annotation.Nullable。随着其他课程的毕业 从javax到Java,过去的javax。注释会 向正确的方向迈出一步。

实现

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

首先是相似之处:

@NonNull注释都有一行

public @interface NonNull {}

除了

它将其命名为@NotNull,并有一个简单的实现 javax。具有较长实现的注释 也叫@NotNull,并且有一个实现

@Nullable注释都有一行

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

如本文所述,SO回答了运行时注释的影响 比人们想象的要小,但他们有好处吗 使工具能够执行运行时检查的 编译时1。

另一个重要的区别是代码中注释的使用位置。 有两种不同的方法。一些包使用JLS 9.6.4.1样式上下文。下表给出了概述:

Package FIELD METHOD PARAMETER LOCAL_VARIABLE
android.support.annotation ✔️ ✔️ ✔️
edu.umd.cs.findbugs.annotations ✔️ ✔️ ✔️ ✔️
org.jetbrains.annotation ✔️ ✔️ ✔️ ✔️
lombok ✔️ ✔️ ✔️ ✔️
javax.validation.constraints ✔️ ✔️ ✔️

org.eclipse.jdt。注释、javax。Annotation和org.checkerframework.checker. nullnness .qual使用中定义的上下文 JLS 4.11,在我看来这是正确的方法。

这就留给我们

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

在这一轮。

Code

为了帮助您自己进一步比较细节,我在下面列出了每个注释的代码。 为了便于比较,我删除了注释、导入和@Documented注释。 (除了Android包中的类,它们都有@Documented)。 我对行和@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。注释至少被Eclipse和Checker框架开箱即用所支持。

总结

我的理想注释是java。注释语法与检查器框架实现。

如果你不打算使用检查器框架,javax。注释(JSR-305)仍然是目前最好的选择。

如果您愿意购买检查器框架,请使用 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