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

发布于 2024-10-16 18:42:18 字数 1432 浏览 3 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(26

风吹短裙飘 2024-10-23 18:42:18

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.constraints > 或javax.annotation
前者随 JEE 一起提供。这是否比 javax.annotation 更好,这可能最终随 JSE 一起出现,或者根本不会出现,这是一个有争议的问题。
我个人更喜欢 javax.annotation,因为我不喜欢 JEE 依赖项。

这给我们留下了

javax.annotation

它也是最短的。

只有一种语法会更好:java.annotation.Nullable。随着其他包的毕业
过去从 javaxjava,javax.annotation 会
是朝着正确方向迈出的一步。

实现

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

首先是相似之处:

@NonNull 注释都具有该行,

public @interface NonNull {}

除了

  • org.jetbrains.annotations ,它称为 @NotNull 并且有一个简单的实现
  • javax.annotation,它有一个更长的实现
  • javax.validation.constraints,它也称为@NotNull并且有一个实现

@可空注释都具有该行,

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 样式上下文。下表给出了概述:

字段方法参数LOCAL_VARIABLE
android.support.annotation✔️✔️✔️
edu.umd.cs.findbugs.annotations✔️✔️✔️✔️
org.jetbrains.annotation✔️✔️✔️✔️
lombok✔️✔️✔️✔️
javax.validation.constraints✔️✔️✔️

org.eclipse.jdt.annotationjavax.annotationorg.checkerframework.checker.nullness.qual 使用中定义的上下文
JLS 4.11,这是我认为正确的做法是正确的。

这使得我们在这一轮中只剩下

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

代码

为了帮助您自己比较更多细节,我在下面列出了每个注释的代码。
为了使比较更容易,我删除了注释、导入和 @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.annotation 至少受到 Eclipse 和 Checker Framework 的开箱即用支持。

总结

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

如果您不打算使用 Checker 框架,则 javax.annotation (JSR-305)目前仍然是您最好的选择。

如果您愿意购买 Checker Framework,只需使用
他们的org.checkerframework.checker.nullness.qual


来源

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

Since JSR 305 (whose goal was to standardize @NonNull and @Nullable) has been dormant for several years, I'm afraid there is no good answer. All we can do is to find a pragmatic solution and mine is as follows:

Syntax

From a purely stylistic standpoint I would like to avoid any reference to IDE, framework or any toolkit except Java itself.

This rules out:

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

Which leaves us with either javax.validation.constraints or javax.annotation.
The former comes with JEE. If this is better than javax.annotation, which might come eventually with JSE or never at all, is a matter of debate.
I personally prefer javax.annotation because I wouldn't like the JEE dependency.

This leaves us with

javax.annotation

which is also the shortest one.

There is only one syntax which would even be better: java.annotation.Nullable. As other packages graduated
from javax to java in the past, the javax.annotation would
be a step in the right direction.

Implementation

I was hoping that they all have basically the same trivial implementation,
but a detailed analysis showed that this is not true.

First for the similarities:

The @NonNull annotations all have the line

public @interface NonNull {}

except for

  • org.jetbrains.annotations which calls it @NotNull and has a trivial implementation
  • javax.annotation which has a longer implementation
  • javax.validation.constraints which also calls it @NotNull and has an implementation

The @Nullableannotations all have the line

public @interface Nullable {}

except for (again) the org.jetbrains.annotations with their trivial implementation.

For the differences:

A striking one is that

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

all have runtime annotations (@Retention(RUNTIME)), while

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

are only compile time (@Retention(CLASS)).

As described in this SO answer the impact of runtime annotations
is smaller than one might think, but they have the benefit
of enabling tools to do runtime checks in addition to the
compile time ones.

Another important difference is where in the code the annotations can be used.
There are two different approaches. Some packages use JLS 9.6.4.1 style contexts. The following table gives an overview:

PackageFIELDMETHODPARAMETERLOCAL_VARIABLE
android.support.annotation✔️✔️✔️
edu.umd.cs.findbugs.annotations✔️✔️✔️✔️
org.jetbrains.annotation✔️✔️✔️✔️
lombok✔️✔️✔️✔️
javax.validation.constraints✔️✔️✔️

org.eclipse.jdt.annotation, javax.annotation and org.checkerframework.checker.nullness.qual use the contexts defined in
JLS 4.11, which is in my opinion the right way to do it.

This leaves us with

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

in this round.

Code

To help you to compare further details yourself I list the code of every annotation below.
To make comparison easier I removed comments, imports and the @Documented annotation.
(they all had @Documented except for the classes from the Android package).
I reordered the lines and @Target fields and normalized the qualifications.

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

For completeness, here are the @Nullable implementations:

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

The following two packages have no @Nullable, so I list them separately; Lombok has a pretty boring @NonNull.
In javax.validation.constraints the @NonNull is actually a @NotNull
and it has a longish implementation.

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

Support

From my experience, javax.annotation is at least supported by Eclipse and the Checker Framework out of the box.

Summary

My ideal annotation would be the java.annotation syntax with the Checker Framework implementation.

If you don't intend to use the Checker Framework the javax.annotation (JSR-305) is still your best bet for the time being.

If you are willing to buy into the Checker Framework just use
their org.checkerframework.checker.nullness.qual.


Sources

  • 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
一页 2024-10-23 18:42:18

我非常喜欢 Checker Framework,它是类型注释的实现(JSR-308),用于实现缺陷检查器,例如空值检查器。我还没有真正尝试过任何其他人来提供任何比较,但我对这个实现很满意。

我不属于提供该软件的组织,但我是该软件的粉丝。

我喜欢这个系统的四点:

  1. 它有一个针对 nullness 的缺陷检查器 (@可空),但也有 不变性实习(和其他)。我使用第一个(无效),并尝试使用第二个(不变性/IGJ)。我正在尝试第三种,但我不确定是否可以长期使用它。我还不相信其他检查器的一般用途,但很高兴知道该框架本身是一个用于实现各种附加注释和检查器的系统。

  2. 空值检查的默认设置效果很好:除局部变量外非空 (NNEL) 。基本上,这意味着默认情况下检查器会将除局部变量之外的所有内容(实例变量、方法参数、泛型类型等)视为默认情况下具有 @NonNull 类型。根据文档:

    <块引用>

    NNEL 默认值会导致代码中显式注释的数量最少。

    如果 NNEL 不适合您,您可以为类或方法设置不同的默认值。

  3. 此框架允许您与一起使用,而无需创建对该框架的依赖< /a> 通过将注释括在注释中:例如 /*@Nullable*/。这很好,因为您可以注释和检查库或共享代码,但仍然能够在不使用该框架的另一个项目中使用该库/共享代码。这是一个很好的功能。我已经习惯了使用它,尽管我现在倾向于在我的所有项目上启用 Checker 框架。

  4. 该框架有一种方法可以注释您使用的尚未注释的 API通过使用存根文件来消除无效性。

I very much like the Checker Framework, which is an implementation of type annotations (JSR-308) which is used to implement defect checkers like a nullness checker. I haven't really tried any others to offer any comparison, but I've been happy with this implementation.

I'm not affiliated with the group that offers the software, but I am a fan.

Four things I like about this system:

  1. It has a defect checkers for nullness (@Nullable), but also has ones for immutability and interning (and others). I use the first one (nullness) and I'm trying to get into using the second one (immutability/IGJ). I'm trying out the third one, but I'm not certain about using it long term yet. I'm not convinced of the general usefulness of the other checkers yet, but its nice to know that the framework itself is a system for implementing a variety of additional annotations and checkers.

  2. The default setting for nullness checking works well: Non-null except locals (NNEL). Basically this means that by default the checker treats everyhing (instance variables, method parameters, generic types, etc) except local variables as if they have a @NonNull type by default. Per the documentation:

    The NNEL default leads to the smallest number of explicit annotations in your code.

    You can set a different default for a class or for a method if NNEL doesn't work for you.

  3. This framework allows you to use with without creating a dependency on the framework by enclosing your annotations in a comment: e.g. /*@Nullable*/. This is nice because you can annotate and check a library or shared code, but still be able to use that library/shared coded in another project that doesn't use the framework. This is a nice feature. I've grown accustom to using it, even though I tend to enable the Checker Framework on all my projects now.

  4. The framework has a way to annotate APIs you use that aren't already annotated for nullness by using stub files.

风柔一江水 2024-10-23 18:42:18

我使用 IntelliJ,因为我主要关心 IntelliJ 标记可能产生 NPE 的事物。我同意 JDK 中没有标准注释是令人沮丧的。有传言要添加它,它可能会进入 Java 7。在这种情况下,就会有更多的选择!

I use the IntelliJ one, because I'm mostly concerned with IntelliJ flagging things that might produce a NPE. I agree that it's frustrating not having a standard annotation in the JDK. There's talk of adding it, it might make it into Java 7. In which case there will be one more to choose from!

深爱成瘾 2024-10-23 18:42:18

根据 Java 7 功能列表 JSR-308 类型注释被推迟到 Java 8甚至没有提到 JSR-305 注释。

附录 中有一些关于 JSR-305 状态的信息最新的 JSR-308 草案。这包括 JSR-305 注释似乎被放弃的观察结果。 JSR-305 页面也将其显示为“非活动”。

同时,务实的答案是使用最广泛使用的工具支持的注释类型……并准备在情况发生变化时更改它们。


事实上,JSR-308 没有定义任何注释类型/类,而且看起来他们认为它超出了范围。 (考虑到 JSR-305 的存在,他们是对的)。

然而,如果 JSR-308 真的看起来像是要融入 Java 8,那么对 JSR-305 的兴趣重新燃起我也不会感到惊讶。 AFAIK,JSR-305 团队还没有正式放弃他们的工作。他们已经安静了两年多了。

有趣的是,Bill Pugh(JSR-305 的技术负责人)是 FindBugs 背后的人之一。

According to the Java 7 features list JSR-308 type annotations are deferred to Java 8. JSR-305 annotations are not even mentioned.

There is a bit of info on the state of JSR-305 in an appendix of the latest JSR-308 draft. This includes the observation that JSR-305 annotations seem to be abandoned. The JSR-305 page also shows it as "inactive".

In the mean time, the pragmatic answer is to use the annotation types that are supported by the most widely used tools ... and be prepared to change them if the situation changes.


In fact, JSR-308 does not define any annotation types/classes, and it looks like they think it is out of scope. (And they are right, given the existence of JSR-305).

However, if JSR-308 really looks like making it into Java 8, it wouldn't surprise me if interest in JSR-305 revived. AFAIK, the JSR-305 team hasn't formally abandoned their work. They have just been quiet for 2+ years.

It is interesting that Bill Pugh (the tech lead for JSR-305) is one of the guy behind FindBugs.

°如果伤别离去 2024-10-23 18:42:18

对于 Android 项目,您应该使用 android.support.annotation.NonNullandroid.support.annotation.Nullable支持库中提供了这些以及其他有用的 Android 特定注释。

来自 http://tools.android.com/tech-docs/support-annotations

支持库本身也带有这些注释
注解,因此作为支持库的用户,Android Studio 会
已经检查您的代码并根据这些标记潜在的问题
注释。

For Android projects you should use android.support.annotation.NonNull and android.support.annotation.Nullable. These and other helpful Android-specific annotations are available in the Support Library.

From http://tools.android.com/tech-docs/support-annotations:

The support library itself has also been annotated with these
annotations, so as a user of the support library, Android Studio will
already check your code and flag potential problems based on these
annotations.

马蹄踏│碎落叶 2024-10-23 18:42:18

区分静态分析和运行时分析。对内部内容使用静态分析,对代码的公共边界使用运行时分析。

对于不应该为 null 的内容:

  • 运行时检查:使用“if (x == null) ...”(零依赖)或 @javax.validation.NotNull(使用 bean 验证)或 @lombok.NonNull(普通)和简单)或番石榴 Preconditions.checkNotNull(...)

    • (仅)对方法返回类型使用Optional。 Java8 或 Guava。
  • 静态检查:使用@NonNull 注释

  • 在合适的地方,在类或包级别使用@...NonnullByDefault 注释。自己创建这些注释(示例很容易找到)。
    • 否则,在方法返回上使用 @...CheckForNull 以避免 NPE

这应该会给出最好的结果:IDE 中的警告、Findbugs 和 checkerframework 的错误、有意义的运行时异常。

不要指望静态检查会成熟,它们的命名不规范,不同的库和 IDE 对待它们的方式不同,忽略它们。 JSR305 javax.annotations.* 类看起来像标准的,但实际上并非如此,并且它们会导致与 Java9+ 的包分割。

一些注释解释:

  • 包 javax.validation.* 的 Findbugs/spotbugs/jsr305 注释与 Java9+ 中的其他模块冲突,也可能违反 Oracle 许可证
  • Spotbugs 注释在编译时仍然依赖于 jsr305/findbugs 注释(在撰写本文时 https://github.com/spotbugs/spotbugs/issues/421)
  • jetbrains @NotNull 名称与 @ 冲突javax.validation.NotNull。
  • 用于静态检查的 jetbrains、eclipse 或 checkersframework 注释比 javax.annotations 具有优势,它们不会与 Java9 及更高版本中的其他模块发生冲突
  • @javax.annotations.Nullable 并不意味着 Findbugs/Spotbugs 你(或你的 IDE)认为它方法。 Findbugs 将忽略它(在成员上)。悲伤,但却是事实(https://sourceforge.net/p/findbugs/bugs/1181)
  • 对于 IDE 外部的静态检查,有 2 个免费工具:Spotbugs(以前称为 Findbugs)和 checkersframework。
  • Eclipse库有@NonNullByDefault,jsr305只有@ParametersAreNonnullByDefault。这些只是将基本注释应用于包(或类)中的所有内容的便捷包装器,您可以轻松创建自己的包装器。这可以在包装上使用。这可能与生成的代码(例如 lombok)冲突。
  • 对于与其他人共享的库,应避免使用 lombok 作为导出依赖项,传递依赖项越少越好。
  • 使用 Bean 验证框架功能强大,但需要很高的开销,因此仅仅为了避免手动 null 检查就显得有些过分了。
  • 对字段和方法参数使用可选是有争议的(您可以轻松找到相关文章)
  • Android null 注释是 Android 支持库的一部分,它们附带了很多其他类,并且不能与其他注释/工具很好地配合

在 Java9 之前,这是我的建议:

// file: package-info.java
@javax.annotation.ParametersAreNonnullByDefault
package example;


// file: PublicApi
package example;

public interface PublicApi {

    Person createPerson(
        // NonNull by default due to package-info.java above
        String firstname,
        String lastname);
}

// file: PublicApiImpl
public class PublicApiImpl implements PublicApi {
    public Person createPerson(
            // In Impl, handle cases where library users still pass null
            @Nullable String firstname, // Users  might send null
            @Nullable String lastname // Users might send null
            ) {
        if (firstname == null) throw new IllagalArgumentException(...);
        if (lastname == null) throw new IllagalArgumentException(...);
        return doCreatePerson(fistname, lastname, nickname);
    }

    @NonNull // Spotbugs checks that method cannot return null
    private Person doCreatePerson(
             String firstname, // Spotbugs checks null cannot be passed, because package has ParametersAreNonnullByDefault
             String lastname,
             @Nullable String nickname // tell Spotbugs null is ok
             ) {
         return new Person(firstname, lastname, nickname);
    }

    @CheckForNull // Do not use @Nullable here, Spotbugs will ignore it, though IDEs respect it
    private Person getNickname(
         String firstname,
         String lastname) {
         return NICKNAMES.get(firstname + ':' + lastname);
    }
}

请注意,当取消引用可为空的方法参数时,没有办法让 Spotbugs 发出警告(在撰写本文时,Spotbugs 版本为 3.1)。也许 checkerframework 可以做到这一点。

遗憾的是,这些注释无法区分具有任意调用点的库的公共方法和每个调用点已知的非公共方法的情况。因此,“指示不需要 null,但仍要为传递 null 做好准备”的双重含义在单个声明中是不可能的,因此上面的示例对接口和实现有不同的注释。

对于拆分接口方法不实用的情况,以下方法是一种折衷方法:

        public Person createPerson(
                @NonNull String firstname,
                @NonNull String lastname
                ) {
            // even though parameters annotated as NonNull, library clients might call with null.
            if (firstname == null) throw new IllagalArgumentException(...);
            if (lastname == null) throw new IllagalArgumentException(...);
            return doCreatePerson(fistname, lastname, nickname);
        }

这有助于客户端不传递 null(编写正确的代码),同时在传递 null 时返回有用的错误。

Distinguish between static analysis and runtime analysis. Use static analysis for internal stuff, and runtime analysis for the public boundaries of your code.

For things that should not be null:

  • Runtime check: Use "if (x == null) ..." (zero dependency) or @javax.validation.NotNull (with bean validation) or @lombok.NonNull (plain and simple) or guavas Preconditions.checkNotNull(...)

    • Use Optional for method return types (only). Either Java8 or Guava.
  • Static check: Use an @NonNull annotation

  • Where it fits, use @...NonnullByDefault annotations on class or package level. Create these annotations yourself (examples are easy to find).
    • Else, use @...CheckForNull on method returns to avoid NPEs

This should give the best result: warnings in the IDE, errors by Findbugs and checkerframework, meaningful runtime exceptions.

Do not expect static checks to be mature, their naming is not standardized and different libraries and IDEs treat them differently, ignore them. The JSR305 javax.annotations.* classes look like standard, but they are not, and they cause split packages with Java9+.

Some notes explanations:

  • Findbugs/spotbugs/jsr305 annotations with package javax.validation.* clash with other modules in Java9+, also possibly violate Oracle license
  • Spotbugs annotations still depends on jsr305/findbugs annotations at compiletime (at the time of writing https://github.com/spotbugs/spotbugs/issues/421)
  • jetbrains @NotNull name conflicts with @javax.validation.NotNull.
  • jetbrains, eclipse or checkersframework annotations for static checking have the advantage over javax.annotations that they do not clash with other modules in Java9 and higher
  • @javax.annotations.Nullable does not mean to Findbugs/Spotbugs what you (or your IDE) think it means. Findbugs will ignore it (on members). Sad, but true (https://sourceforge.net/p/findbugs/bugs/1181)
  • For static checking outside an IDE, 2 free tools exist: Spotbugs(formerly Findbugs) and checkersframework.
  • The Eclipse library has @NonNullByDefault, jsr305 only has @ParametersAreNonnullByDefault. Those are mere convenience wrappers applying base annotations to everything in a package (or class), you can easily create your own. This can be used on package. This may conflict with generated code (e.g. lombok).
  • Using lombok as an exported dependency should be avoided for libraries that you share with other people, the less transitive dependencies, the better
  • Using Bean validation framework is powerful, but requires high overhead, so that's overkill just to avoid manual null checking.
  • Using Optional for fields and method parameters is controversial (you can find articles about it easily)
  • Android null annotations are part of the Android support library, they come with a whole lot of other classes, and don't play nicely with other annotations/tools

Before Java9, this is my recommendation:

// file: package-info.java
@javax.annotation.ParametersAreNonnullByDefault
package example;


// file: PublicApi
package example;

public interface PublicApi {

    Person createPerson(
        // NonNull by default due to package-info.java above
        String firstname,
        String lastname);
}

// file: PublicApiImpl
public class PublicApiImpl implements PublicApi {
    public Person createPerson(
            // In Impl, handle cases where library users still pass null
            @Nullable String firstname, // Users  might send null
            @Nullable String lastname // Users might send null
            ) {
        if (firstname == null) throw new IllagalArgumentException(...);
        if (lastname == null) throw new IllagalArgumentException(...);
        return doCreatePerson(fistname, lastname, nickname);
    }

    @NonNull // Spotbugs checks that method cannot return null
    private Person doCreatePerson(
             String firstname, // Spotbugs checks null cannot be passed, because package has ParametersAreNonnullByDefault
             String lastname,
             @Nullable String nickname // tell Spotbugs null is ok
             ) {
         return new Person(firstname, lastname, nickname);
    }

    @CheckForNull // Do not use @Nullable here, Spotbugs will ignore it, though IDEs respect it
    private Person getNickname(
         String firstname,
         String lastname) {
         return NICKNAMES.get(firstname + ':' + lastname);
    }
}

Note that there is no way to make Spotbugs raise a warning when a nullable method parameter is dereferenced (at the time of writing, version 3.1 of Spotbugs). Maybe checkerframework can do that.

Sadly these annotations do not distinguish between the cases of a public method of a library with arbitrary callsites, and non-public methods where each callsite can be known. So the double meaning of: "Indicate that null is undesired, but prepare for null being passed nevertheless" is not possible in a single declaration, hence the above example has different annotations for the interface and the implementation.

For cases where the split interface approach is not practical, the following approach is a compromise:

        public Person createPerson(
                @NonNull String firstname,
                @NonNull String lastname
                ) {
            // even though parameters annotated as NonNull, library clients might call with null.
            if (firstname == null) throw new IllagalArgumentException(...);
            if (lastname == null) throw new IllagalArgumentException(...);
            return doCreatePerson(fistname, lastname, nickname);
        }

This helps clients to not pass null (writing correct code), while returning useful errors if they do.

月下凄凉 2024-10-23 18:42:18

如果有人只是在寻找 IntelliJ 类:您可以使用以下命令从 Maven 存储库中获取它们

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations</artifactId>
    <version>15.0</version>
</dependency> 

If anyone is just looking for the IntelliJ classes: you can get them from the maven repository with

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations</artifactId>
    <version>15.0</version>
</dependency> 
隱形的亼 2024-10-23 18:42:18

JSR305和FindBugs是同一个人编写的。两者都维护得很差,但都是标准的,并且受到所有主要 IDE 的支持。好消息是它们按原样运行良好。

以下是如何默认将 @Nonnull 应用于所有类、方法和字段。
请参阅 https://stackoverflow.com/a/13319541/14731https://stackoverflow.com/a/9256595/14731

  1. 定义@NotNullByDefault
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;


    /**
     * This annotation can be applied to a package, class or method to indicate that the class fields,
     * method return types and parameters in that element are not null by default unless there is: <ul>
     * <li>An explicit nullness annotation <li>The method overrides a method in a superclass (in which
     * case the annotation of the corresponding parameter in the superclass applies) <li> there is a
     * default parameter annotation applied to a more tightly nested element. </ul>
     * <p/>
     * @see https://stackoverflow.com/a/9256595/14731
     */
    @Documented
    @Nonnull
    @TypeQualifierDefault(
    {
        ElementType.ANNOTATION_TYPE,
        ElementType.CONSTRUCTOR,
        ElementType.FIELD,
        ElementType.LOCAL_VARIABLE,
        ElementType.METHOD,
        ElementType.PACKAGE,
        ElementType.PARAMETER,
        ElementType.TYPE
    })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface NotNullByDefault
    {
    }

2.为每个包添加注解:package- info.java

@NotNullByDefault
package com.example.foo;

更新:截至 2012 年 12 月 12 日 JSR 305 被列为“休眠”。根据文档:

被执行委员会投票为“休眠”的 JSR,或者已达到自然寿命终点的 JSR。

看起来JSR 308正在将其纳入JDK 8,尽管JSR确实不定义@NotNull,附带的Checkers Framework 会定义。在撰写本文时,Maven 插件由于以下错误而无法使用:https:// /github.com/typetools/checker-framework/issues/183

JSR305 and FindBugs are authored by the same person. Both are poorly maintained but are as standard as it gets and are supported by all major IDEs. The good news is that they work well as-is.

Here is how to apply @Nonnull to all classes, methods and fields by default.
See https://stackoverflow.com/a/13319541/14731 and https://stackoverflow.com/a/9256595/14731

  1. Define @NotNullByDefault
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;


    /**
     * This annotation can be applied to a package, class or method to indicate that the class fields,
     * method return types and parameters in that element are not null by default unless there is: <ul>
     * <li>An explicit nullness annotation <li>The method overrides a method in a superclass (in which
     * case the annotation of the corresponding parameter in the superclass applies) <li> there is a
     * default parameter annotation applied to a more tightly nested element. </ul>
     * <p/>
     * @see https://stackoverflow.com/a/9256595/14731
     */
    @Documented
    @Nonnull
    @TypeQualifierDefault(
    {
        ElementType.ANNOTATION_TYPE,
        ElementType.CONSTRUCTOR,
        ElementType.FIELD,
        ElementType.LOCAL_VARIABLE,
        ElementType.METHOD,
        ElementType.PACKAGE,
        ElementType.PARAMETER,
        ElementType.TYPE
    })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface NotNullByDefault
    {
    }

2. Add the annotation to each package: package-info.java

@NotNullByDefault
package com.example.foo;

UPDATE: As of December 12th, 2012 JSR 305 is listed as "Dormant". According to the documentation:

A JSR that was voted as "dormant" by the Executive Committee, or one that has reached the end of its natural lifespan.

It looks like JSR 308 is making it into JDK 8 and although the JSR does not define @NotNull, the accompanying Checkers Framework does. At the time of this writing, the Maven plugin is unusable due to this bug: https://github.com/typetools/checker-framework/issues/183

错爱 2024-10-23 18:42:18

JSpecify 将是可行的方法(当它足够准备好时)。事实上:他们的演示文稿主动链接到这个问题,并指定他们的目标是最终得到一个好的答案。

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

JSpecify will be the way to go (when it's ready enough). In fact: their presentation actively links to this very question and specifies that their goal is for it to finally have a good answer.

It has major participants like Android, Guava and Kotlin.

寄风 2024-10-23 18:42:18

较新的项目可能应该使用 jakarta.annotation-api< /a>(jakarta.annotation 包)。
它从现在开始链接为只读
javax.annotation repo 并适合新的 jakarta 生态系统让社区摆脱所有与 javax 相关的麻烦。

newer projects should probably use jakarta.annotation-api (jakarta.annotation package).
It is linked from now read-only javax.annotation repo and fits into the new jakarta ecosystem that aims to free the community from all the javax related headaches.

紫罗兰の梦幻 2024-10-23 18:42:18

Eclipse 也有自己的注释。

org.eclipse.jdt.annotation.NonNull

有关详细信息,请参阅 http://wiki.eclipse.org/JDT_Core/Null_Analysis

Eclipse has also its own annotations.

org.eclipse.jdt.annotation.NonNull

See at http://wiki.eclipse.org/JDT_Core/Null_Analysis for details.

深空失忆 2024-10-23 18:42:18

只是指出 Java 验证 API (javax.validation.constraints.*) 没有附带 @Nullable 注释,这在静态分析上下文中非常有价值。这对于运行时 bean 验证很有意义,因为这是 Java 中任何非原始字段的默认值(即无需验证/强制执行)。出于所述目的,应权衡替代方案。

Just pointing out that the Java Validation API (javax.validation.constraints.*) doesn't come with a @Nullable annotation, which is very valuable in a static analysis context. It makes sense for runtime bean validation as this is the default for any non-primitive field in Java (i.e. nothing to validate/enforce). For the purposes stated that should weigh towards the alternatives.

别理我 2024-10-23 18:42:18

这里已经有太多答案了,但是(a)已经是 2019 年了,仍然没有“标准”Nullable 以及(b)没有其他答案引用 Kotlin。

对 Kotlin 的引用很重要,因为 Kotlin 与 Java 100% 可互操作,并且它具有核心 Null Safety 功能。调用 Java 库时,它可以利用这些注释让 Kotlin 工具知道 Java API 是否可以接受或返回 null

据我所知,唯一与 Kotlin 兼容的 Nullable 包是 org.jetbrains.annotationsandroid.support.annotation (现在 >androidx.annotation)。后者仅与 Android 兼容,因此不能在非 Android JVM/Java/Kotlin 项目中使用。然而,JetBrains 软件包可以在任何地方使用。

因此,如果您开发的 Java 包也应该在 Android 和 Kotlin 中工作(并且受 Android Studio 和 IntelliJ 支持),那么您的最佳选择可能是 JetBrains 包。

Maven:

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations-java5</artifactId>
    <version>15.0</version>
</dependency>

摇篮:

implementation 'org.jetbrains:annotations-java5:15.0'

There are already too many answers here, but (a) it's 2019, and there's still no "standard" Nullable and (b) no other answer references Kotlin.

The reference to Kotlin is important, because Kotlin is 100% interoperable with Java and it has a core Null Safety feature. When calling Java libraries, it can take advantage of those annotations to let Kotlin tools know if a Java API can accept or return null.

As far as I know, the only Nullable packages compatible with Kotlin are org.jetbrains.annotations and android.support.annotation (now androidx.annotation). The latter is only compatible with Android so it can't be used in non-Android JVM/Java/Kotlin projects. However, the JetBrains package works everywhere.

So if you develop Java packages that should also work in Android and Kotlin (and be supported by Android Studio and IntelliJ), your best choice is probably the JetBrains package.

Maven:

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations-java5</artifactId>
    <version>15.0</version>
</dependency>

Gradle:

implementation 'org.jetbrains:annotations-java5:15.0'
清泪尽 2024-10-23 18:42:18

关于为什么选择 jspecify 的更多详细信息:

jspecify 正在由一个领导小组创建通过 Google:

EISOP 团队,
谷歌,
JetBrains,
元,
微软,
甲骨文、PMD 团队、
声纳,
正方形 ,
优步,
威睿。

他们的目标是创建一个独立于工具的无效注释标准。

解答共识中列出了(最终)使用它的原因

  • 常见问题
  • 他们在精确定义的
  • 不如很棒的检查器框架严格(复杂、难以迁移、误报)
  • 不是特定于工具的

稳定 API 版本 1.0.0 已于 2024 年 7 月 17 日发布。

A bit more details on why to choose jspecify:

jspecify, is being created by a group led by Google:

EISOP Team,
Google,
JetBrains,
Meta ,
Microsoft ,
Oracle ,PMD Team ,
Sonar ,
Square ,
Uber ,
VMware .

They aim to create a tool-independent nullness annotation standard.

They list the reasons to (eventually) use it in their FAQ

  • consensus
  • precisely defined
  • less strict than the awesome checker framework (complex, hard to migrate, false positives)
  • not tool-specific

Stable API version 1.0.0 has been released on July 17, 2024.

硬不硬你别怂 2024-10-23 18:42:18

不幸的是,JSR 308 不会添加比该项目本地 Not Null 建议更多的值

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

其背后没有商业力量,因此 JSR 308 立即启动 EDR 3JCP 的早期草案审查),而 Java 8 code> 预计在 6 个月内发货:-O
顺便说一句,类似于 310。但与 308 不同的是,Oracle 现在已经从其创始人手中接管了这一事务,以最大程度地减少它对 Java 平台造成的损害。

每个项目、供应商和学术课程(例如 Checker FrameworkJSR 308 背后的项目、供应商和学术课程)都将创建自己的专有检查器注释。

在未来几年内使源代码不兼容,直到找到一些流行的妥协方案并可能添加到 Java 9 或 10 中,或者通过 Apache Commons 等框架添加/code> 或 Google Guava;-)

Unfortunately, JSR 308 will not add more values than this project local Not Null suggestion here

Java 8 will not come with a single default annotation or its own Checker framework.
Similar to Find-bugs or JSR 305, this JSR is poorly maintained by a small bunch of mostly academic teams.

No commercial power behind it, thus JSR 308 launches EDR 3 (Early Draft Review at JCP) NOW, while Java 8 is supposed to ship in less than 6 months:-O
Similar to 310 btw. but unlike 308 Oracle has taken charge of that now away from its founders to minimize harm it'll do to the Java Platform.

Every project, vendor and academic class like the ones behind the Checker Framework and JSR 308 will create its own proprietary checker annotation.

Making source code incompatible for years to come, until a few popular compromises could be found and maybe added to Java 9 or 10, or via frameworks like Apache Commons or Google Guava;-)

轮廓§ 2024-10-23 18:42:18

Android

这个答案是 Android 特定的。 Android 有一个名为 support-annotations 的支持包。这提供了数十Android 特定 注释,还提供 常见的,例如 NonNullNullable 等。

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

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

然后使用:

import android.support.annotation.NonNull;

void foobar(@NonNull Foo bar) {}

Android

This answer is Android specific. Android has support package called support-annotations. This provides dozens of Android specific annotations and also provides common ones like NonNull, Nullable etc.

To add support-annotations package, add the following dependency in your build.gradle:

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

and then use:

import android.support.annotation.NonNull;

void foobar(@NonNull Foo bar) {}
不及他 2024-10-23 18:42:18

如果您正在开发一个大型项目,您最好创建您自己的 @Nullable 和/或 @NotNull 注释。

例如:

@java.lang.annotation.Documented
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS)
@java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD,
                              java.lang.annotation.ElementType.METHOD,    
                              java.lang.annotation.ElementType.PARAMETER,
                              java.lang.annotation.ElementType.LOCAL_VARIABLE})
public @interface Nullable 
{
}

如果您使用正确的保留策略,则注释将不会在运行时可用。从这个角度来看,这只是一个内部的事情。

尽管这不是一门严格的科学,但我认为使用内部类是最有意义的。

  • 这是一个内在的事情。 (没有功能或技术影响)
  • 有很多很多用途。
  • 像 IntelliJ 这样的 IDE 支持自定义 @Nullable/@NotNull 注释。
  • 大多数框架也更喜欢使用自己的内部版本。

其他问题(请参阅评论):

如何在 IntelliJ 中进行配置?

点击IntelliJ状态栏右下角的“警察”。然后在弹出窗口中单击“配置检查”。下一个 ...
配置注释

If you are working on a big project, you may be better of creating your own @Nullable and/or @NotNull annotations.

For example:

@java.lang.annotation.Documented
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS)
@java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD,
                              java.lang.annotation.ElementType.METHOD,    
                              java.lang.annotation.ElementType.PARAMETER,
                              java.lang.annotation.ElementType.LOCAL_VARIABLE})
public @interface Nullable 
{
}

If you use the correct retention policy, then the annotations won't be available at runtime. From that point of view, it is just an internal thing.

Even though this is not a strict science, I think it makes most sense to use an internal class for it.

  • It is an internal thing. (no functional or technical impact)
  • With many many many usages.
  • IDE's like IntelliJ support custom @Nullable/@NotNull annotations.
  • Most frameworks prefer to use their own internal version as well.

Additional Questions (see comments):

How to configure this in IntelliJ ?

Click the "police officer" in the lower right corner of the IntelliJ status bar. And click "Configure inspections" in the popup. Next ...
configure annotations

菩提树下叶撕阳。 2024-10-23 18:42:18

在 Java 8 中还有另一种方法可以做到这一点。
我正在做两件事来完成我所需要的:

  1. 通过使用 java.util.Optional 包装可空字段,使可空字段在类型中显式使用
  2. 检查所有不可空字段在构造时不为空java.util.Objects.requireNonNull

示例:

编辑:忽略第一个示例,我只是将此处作为评论对话的上下文。此后跳至推荐选项(第二个代码块)。

    import static java.util.Objects.requireNonNull;

    public class Role {

      private final UUID guid;
      private final String domain;
      private final String name;
      private final Optional<String> description;

      public Role(UUID guid, String domain, String name, Optional<String> description) {
        this.guid = requireNonNull(guid);
        this.domain = requireNonNull(domain);
        this.name = requireNonNull(name);
        this.description = requireNonNull(description);
      }
   }

所以我的问题是,我们在使用java 8时还需要注释吗?

编辑:我后来发现有些人认为在参数中使用 Optional 是一种不好的做法,这里有一个关于优点和缺点的很好的讨论 为什么 Java 8 的Optional 不能在参数中使用

推荐的选项,因为在中使用Optional 并不是最佳实践参数,我们需要 2 个构造函数:

public class Role {
      
      // Required fields, will not be null (unless using reflection) 
      private final UUID guid;
      private final String domain;
      private final String name;
      // Optional field, not null but can be empty
      private final Optional<String> description;

  //Non null description
  public Role(UUID guid, String domain, String name, String description) {
        this.guid = requireNonNull(guid);
        this.domain = requireNonNull(domain);
        this.name = requireNonNull(name);

        // description will never be null
        requireNonNull(description);

        // but wrapped with an Optional
        this.description = Optional.of(description);
      }

  // Null description is assigned to Optional.empty
  public Role(UUID guid, String domain, String name) {
        this.guid = requireNonNull(guid);
        this.domain = requireNonNull(domain);
        this.name = requireNonNull(name);
        this.description = Optional.empty();
      }
  //Note that this accessor is not a getter.
  //I decided not to use the "get" suffix to distinguish from "normal" getters 
  public Optional<String> description(){ return description;} 
 }

There is another way to do this in Java 8.
I am doing 2 things to accomplish what I needed:

  1. Making nullable fields explicit with types by wrapping nullable fields with java.util.Optional
  2. Checking that all non nullable fields are not null at construction time with java.util.Objects.requireNonNull

Example:

Edit: Disregard this 1st example, I'm just leaving here as context of the comments conversation. Skip to recommended option after this (2nd code block).

    import static java.util.Objects.requireNonNull;

    public class Role {

      private final UUID guid;
      private final String domain;
      private final String name;
      private final Optional<String> description;

      public Role(UUID guid, String domain, String name, Optional<String> description) {
        this.guid = requireNonNull(guid);
        this.domain = requireNonNull(domain);
        this.name = requireNonNull(name);
        this.description = requireNonNull(description);
      }
   }

So my question is, do we even need to annotate when using java 8?

Edit: I found out later that some consider a bad practice to use Optional in arguments, there is a good discussion with pros and cons here Why should Java 8's Optional not be used in arguments

Recommended option given that it is not best practice to use Optional in arguments, we need 2 constructors:

public class Role {
      
      // Required fields, will not be null (unless using reflection) 
      private final UUID guid;
      private final String domain;
      private final String name;
      // Optional field, not null but can be empty
      private final Optional<String> description;

  //Non null description
  public Role(UUID guid, String domain, String name, String description) {
        this.guid = requireNonNull(guid);
        this.domain = requireNonNull(domain);
        this.name = requireNonNull(name);

        // description will never be null
        requireNonNull(description);

        // but wrapped with an Optional
        this.description = Optional.of(description);
      }

  // Null description is assigned to Optional.empty
  public Role(UUID guid, String domain, String name) {
        this.guid = requireNonNull(guid);
        this.domain = requireNonNull(domain);
        this.name = requireNonNull(name);
        this.description = Optional.empty();
      }
  //Note that this accessor is not a getter.
  //I decided not to use the "get" suffix to distinguish from "normal" getters 
  public Optional<String> description(){ return description;} 
 }
十年九夏 2024-10-23 18:42:18

在等待上游解决这个问题(Java 8?)时,​​您也可以定义自己的项目本地 @NotNull@Nullable 注释。如果您使用 Java SE,这也很有用,其中 javax.validation.constraints 默认情况下不可用

import java.lang.annotation.*;

/**
 * Designates that a field, return value, argument, or variable is
 * guaranteed to be non-null.
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
@Documented
@Retention(RetentionPolicy.CLASS)
public @interface NotNull {}

/**
 * Designates that a field, return value, argument, or variable may be null.
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
@Documented
@Retention(RetentionPolicy.CLASS)
public @interface Nullable {}

诚然,这在很大程度上是出于装饰或面向未来的目的,因为上面的内容显然本身并没有添加对这些注释的静态分析的任何支持。

While waiting for this to be sorted out upstream (Java 8?), you could also just define your own project-local @NotNull and @Nullable annotations. This can be useful also in case you're working with Java SE, where javax.validation.constraints isn't available by default.

import java.lang.annotation.*;

/**
 * Designates that a field, return value, argument, or variable is
 * guaranteed to be non-null.
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
@Documented
@Retention(RetentionPolicy.CLASS)
public @interface NotNull {}

/**
 * Designates that a field, return value, argument, or variable may be null.
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
@Documented
@Retention(RetentionPolicy.CLASS)
public @interface Nullable {}

This would admittedly largely be for decorative or future-proofing purposes, since the above obviously doesn't in and of itself add any support for the static analysis of these annotations.

命比纸薄 2024-10-23 18:42:18

如果您使用 Spring Framework 构建应用程序,我建议使用来自 Beans Validation 封装在以下依赖中:

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>

该注解的主要优点是 Spring 提供对使用 javax.validation.constraints.NotNull 注解的方法参数和类字段的支持。要启用支持,您所需要做的就是:

  1. 提供用于 beans 验证的 api jar 和带有 jsr-303/jsr-349 注释验证器实现的 jar(随 Hibernate Validator 5.x 依赖项一起提供):

    <依赖>;
        javax.validation;
        validation-api;
        <版本>1.1.0.最终版
    
    <依赖关系>
        org.hibernate;
        hibernate-validator;
        <版本>5.4.1.最终版
    
    
  2. 向 spring 上下文提供 MethodValidationPostProcessor

    <前><代码> @Configuration
    @ValidationConfig
    公共类 ValidationConfig 实现 MyService {

    @豆
    公共方法验证后处理器提供后处理器(){
    返回新的 MethodValidationPostProcessor()
    }
    }

  3. 最后,使用 Spring 的 org.springframework.validation.annotation.Validated 验证将由Spring。

示例:

@Service
@Validated
public class MyServiceImpl implements MyService {

  @Override
  public Something doSomething(@NotNull String myParameter) {
        // No need to do something like assert myParameter != null  
  }
}

当您尝试调用方法 doSomething 并传递 null 作为参数值时,spring(通过 HibernateValidator)将抛出 ConstraintViolationException。这里不需要手动工作。

您还可以验证返回值。

javax.validation.constraints.NotNull 加入 Beans Validation Framework 的另一个重要好处是,目前它仍在开发中,并且计划在新版本 2.0 中添加新功能。

@Nullable 怎么样? Beans Validation 1.1 中没有类似的内容。好吧,我可以说,如果您决定使用 @NotNull ,那么所有未用 @NonNull 注释的内容实际上都是“可空的”,因此 @Nullable< /code> 注释没有用。

If you are building your application using Spring Framework I would suggest using javax.validation.constraints.NotNull comming from Beans Validation packaged in following dependency:

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>

The main advantage of this annotation is that Spring provides support for both method parameters and class fields annotated with javax.validation.constraints.NotNull. All you need to do to enable support is:

  1. supply the api jar for beans validation and jar with implementation of validator of jsr-303/jsr-349 annotations (which comes with Hibernate Validator 5.x dependency):

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.4.1.Final</version>
    </dependency>
    
  2. provide MethodValidationPostProcessor to spring's context

      @Configuration
      @ValidationConfig
      public class ValidationConfig implements MyService {
    
            @Bean
            public MethodValidationPostProcessor providePostProcessor() {
                  return new MethodValidationPostProcessor()
            }
      }
    
  3. finally you annotate your classes with Spring's org.springframework.validation.annotation.Validated and validation will be automatically handled by Spring.

Example:

@Service
@Validated
public class MyServiceImpl implements MyService {

  @Override
  public Something doSomething(@NotNull String myParameter) {
        // No need to do something like assert myParameter != null  
  }
}

When you try calling method doSomething and pass null as the parameter value, spring (by means of HibernateValidator) will throw ConstraintViolationException. No need for manuall work here.

You can also validate return values.

Another important benefit of javax.validation.constraints.NotNull comming for Beans Validation Framework is that at the moment it is still developed and new features are planned for new version 2.0.

What about @Nullable? There is nothing like that in Beans Validation 1.1. Well, I could arguee that if you decide to use @NotNull than everything which is NOT annotated with @NonNull is effectively "nullable", so the @Nullable annotation is useless.

別甾虛僞 2024-10-23 18:42:18

IntelliJ 的好处之一是您不需要使用它们的注释。您可以自己编写,也可以使用您喜欢的任何其他工具。您甚至不限于单一类型。如果您使用的两个库使用不同的 @NotNull 注释,您可以告诉 IntelliJ 使用这两个库。为此,请转到“配置检查”,单击“常量条件和例外”检查,然后单击“配置检查”按钮。我尽可能使用 Nullness Checker,因此我设置 IntelliJ 来使用这些注释,但您可以让它与您想要的任何其他工具一起使用。 (我对其他工具没有意见,因为我多年来一直使用 IntelliJ 的检查,并且我喜欢它们。)

One of the nice things about IntelliJ is that you don't need to use their annotations. You can write your own, or you can use those of whatever other tool you like. You're not even limited to a single type. If you're using two libraries that use different @NotNull annotations, you can tell IntelliJ to use both of them. To do this, go to "Configure Inspections", click on the "Constant Conditions & Exceptions" inspection, and hit the "Configure inspections" button. I use the Nullness Checker wherever I can, so I set up IntelliJ to use those annotations, but you can make it work with whatever other tool you want. (I have no opinion on the other tools because I've been using IntelliJ's inspections for years, and I love them.)

╭⌒浅淡时光〆 2024-10-23 18:42:18

如果您正在为 Android 进行开发,那么您在某种程度上会依赖 Eclipse(编辑:在撰写本文时,不再是了),它有自己的注释。它包含在 Eclipse 3.8+ (Juno) 中,但默认情况下处于禁用状态。

您可以在“首选项”>“启用它”爪哇>编译器>错误/警告>空分析(底部可折叠部分)。

选中“启用基于注释的空分析”

http://wiki.eclipse.org/JDT_Core/Null_Analysis#使用有设置建议。但是,如果您的工作区中有外部项目(例如 facebook SDK),它们可能不满足这些建议,并且您可能不想在每次 SDK 更新时修复它们;-)

我使用:

  1. 空指针访问:错误
  2. 违规空规范:错误(链接到第 #1 点)
  3. 潜在的空指针访问:警告(否则 facebook SDK 会出现警告)
  4. 空注释和空推断之间的冲突:警告(链接到第 #3 点)

If you're developing for android, you're somewhat tied to Eclipse (edit: at time of writing, not anymore), which has its own annotations. It's included in Eclipse 3.8+ (Juno), but disabled by default.

You can enable it at Preferences > Java > Compiler > Errors/Warnings > Null analysis (collapsable section at the bottom).

Check "Enable annotation-based null analysis"

http://wiki.eclipse.org/JDT_Core/Null_Analysis#Usage has recommendations on settings. However, if you have external projects in your workspace (like the facebook SDK), they may not satisfy those recommendations, and you probably don't want to fix them with each SDK update ;-)

I use:

  1. Null pointer access: Error
  2. Violation of null specification: Error (linked to point #1)
  3. Potential null pointer access: Warning (otherwise facebook SDK would have warnings)
  4. Conflict between null annotations and null inference: Warning (linked to point #3)
暮年慕年 2024-10-23 18:42:18

如果您使用 Spring 5.x / SpringBoot 2.x,您绝对应该使用 Spring 注释 (org.springframework.lang),因为它们通过注释 @NonNullFields@NonNullApi。当您使用 @NonNullFields/@NonNullApi 时,您甚至不会与其他依赖项中的其他 NotNull/NonNull 注释发生冲突。注释必须在名为 package-info.java 的文件中使用,该文件位于包的根目录中:

@NonNullFields
@NonNullApi
package org.company.test;

要从 null 检查中排除某些字段、参数或返回值,只需显式使用 @Nullable< /代码> 注释。您也可以在任何地方设置 @NonNull,而不是使用 @NonNullFields/@NonNullApi,但最好使用 @NonNullFields/@NonNullApi 激活 null 检查默认情况下,仅使用 @Nullable 执行特定异常。

IDE (Intellij) 将突出显示违反 null 条件的代码。如果设置正确,每个开发人员都可以假设字段、参数和返回值必须不为 null,除非有 @Nullable 注解。如需了解更多信息,请查看这篇文章

If you are using Spring 5.x / SpringBoot 2.x you should definitely use the Spring annotations (org.springframework.lang), as they provide you a default package-wide null check with the annotations @NonNullFields and @NonNullApi. You will not even have a clash with other NotNull/NonNull annotations from other dependencies, as you are using @NonNullFields/@NonNullApi. The annotations must be used in a file called package-info.java which is placed in the root directory of the package:

@NonNullFields
@NonNullApi
package org.company.test;

To exclude certain fields, parameters, or return values from the null check, just explicitly use the @Nullable annotation. Instead of using @NonNullFields/@NonNullApi you could also set @NonNull everywhere, but probably it is better to activate null checks with @NonNullFields/@NonNullApi by default and only do specific exceptions with @Nullable.

The IDE (Intellij) will highlight code that violates the null conditions. If set up correctly, every developer can assume that fields, parameters and return values must be not null, unless there is a @Nullable annotation. For more information check out this article.

网白 2024-10-23 18:42:18

现在sun没有自己的了吗?这是什么:
http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Modules-com.sun/istack/com.sun.istack.internal.htm

这似乎是用过去几年我使用过的所有 Java 版本。

编辑:正如下面的评论中提到的,您可能不想使用这些。在这种情况下,我投票给 IntelliJ jetbrains 注释!

Doesn't sun have their own now? What's this:
http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Modules-com.sun/istack/com.sun.istack.internal.htm

This seems to be packaged with all the versions of Java I've used within the last few years.

Edit: As mentioned in the comments below, you probably don't want to use these. In that case, my vote is for the IntelliJ jetbrains annotations!

寄人书 2024-10-23 18:42:18

Spring 5 在包级别有@NonNullApi。对于已经具有 Spring 依赖项的项目来说,这似乎是一个方便的选择。所有字段、参数和返回值默认为 @NonNull 和 @Nullable 可以应用于少数不同的地方。

文件 package-info.java:

@org.springframework.lang.NonNullApi
package com.acme;

https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories.nullability.annotations

Spring 5 has @NonNullApi at the package level. This seems like a convenient choice for a project that already has Spring dependencies. All fields, parameters and return values default to @NonNull and @Nullable can be applied in the few places that differ.

File package-info.java:

@org.springframework.lang.NonNullApi
package com.acme;

https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories.nullability.annotations

成熟的代价 2024-10-23 18:42:18

另一种选择是 ANTLR 4 提供的注释。在 Pull Request #434 之后,包含以下内容的工件@NotNull@Nullable 注解包含一个注解处理器,如果这些属性之一被误用(例如,如果两者都使用),该处理器会生成编译时错误和/或警告应用于同一个项目,或者如果 @Nullable 应用于具有原始类型的项目)。注释处理器在软件开发过程中提供额外的保证,确保这些注释的应用所传达的信息是准确的,包括在方法继承的情况下。

Another option is the annotations provided with ANTLR 4. Following Pull Request #434, the artifact containing the @NotNull and @Nullable annotations includes an annotation processor that produces compile-time errors and/or warnings in the event one of these attributes is misused (for example, if both are applied to the same item, or if @Nullable is applied to item with a primitive type). The annotation processor provides additional assurance during the software development process that the information conveyed by the application of these annotations is accurate, including in cases of method inheritance.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文