我希望使我的代码更具可读性,并使用 IDE 代码检查和/或静态代码分析(FindBugs 和 Sonar)等工具来避免 NullPointerExceptions。许多工具似乎与彼此的 @NotNull
/@NonNull
/@Nonnull
注释不兼容,并且在我的代码中列出所有这些工具将很难阅读。关于哪一个是“最好的”的任何建议?这是我找到的等效注释列表:
javax.validation.constraints.NotNull 创建用于运行时验证,而不是静态分析。文件
edu.umd.cs.findbugs.annotations.NonNull 由 FindBugs(死项目)及其后继 SpotBugs 静态分析和 Sonar(现为 Sonarqube)使用 FindBugs 文档、SpotBugs 文档
javax.annotation.Nonnull 这可能也适用于 FindBugs,但 JSR-305 处于非活动状态。 (另请参阅:JSR 305 的状态是什么?)来源
org.jetbrains.annotations.NotNull 由 IntelliJ IDEA IDE 用于静态分析。文件
lombok.NonNull 用于控制 Project Lombok 中的代码生成。占位符注释,因为没有标准。来源,文档
androidx.annotation.NonNull Marker 注解在Android中可用,由注解包文档提供
org.eclipse.jdt.annotation.NonNull 被 Eclipse 用于静态代码分析文档
com.sun.istack.internal.NotNull
。我的天啊...
由于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.NonNull
这给我们留下了 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 {}
除了
org.jetbrains.annotations 将其称为 @NotNull 并有一个简单的实现
javax.annotation 有更长的实现
javax.validation.constraints 也称它为@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)
)。
如 this SO answer 中所述,运行时注释的影响比人们想象的要小,但它们的好处是使工具能够在编译时检查之外进行运行时检查。
另一个重要的区别是可以在代码中的何处使用注释。有两种不同的方法。一些包使用 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.annotation
、javax.annotation
和 org.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
,所以我单独列出;龙目岛有一个很无聊的@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();
}
}
支持
根据我的经验,Eclipse 和开箱即用的 Checker Framework 至少支持 javax.annotation
。
概括
我理想的注释是带有 Checker Framework 实现的 java.annotation
语法。
如果您不打算使用 Checker Framework,那么 javax.annotation
(JSR-305) 仍然是您目前最好的选择。
如果您愿意购买 Checker Framework,只需使用他们的 org.checkerframework.checker.nullness.qual
。
来源
来自 android-5.1.1_r1.jar 的 android.support.annotation
edu.umd.cs.findbugs.annotations 来自 findbugs-annotations-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
来自 gwt-dev-2.5.1-sources.jar 的 javax.annotation
org.checkerframework.checker.nullness.qual 来自 checker-framework-2.1.9.zip
来自 lombok 的 lombok 提交 f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
来自 validation-api-1.0.0.GA-sources.jar 的 javax.validation.constraints
我非常喜欢 Checker Framework,它是类型注释 (JSR-308) 的实现,用于实现缺陷检查器,如空值检查器。我还没有真正尝试过任何其他人来提供任何比较,但我对这个实现很满意。
我不隶属于提供该软件的团体,但我是粉丝。
我喜欢这个系统的四件事:
它有一个空值缺陷检查器(@Nullable),但也有一个用于不变性和实习(以及其他)的缺陷检查器。我使用第一个(nullness),我正在尝试使用第二个(不变性/IGJ)。我正在尝试第三个,但我不确定是否要长期使用它。我还不相信其他检查器的一般用途,但很高兴知道框架本身是一个用于实现各种附加注释和检查器的系统。空值检查的默认设置效果很好:非空值,除了局部变量 (NNEL)。基本上这意味着默认情况下,检查器将除局部变量之外的所有内容(实例变量、方法参数、泛型类型等)都视为默认情况下具有 @NonNull 类型。根据文档:NNEL 默认会导致代码中显式注释的数量最少。如果 NNEL 不适合您,您可以为类或方法设置不同的默认值。该框架允许您使用 with 而无需创建对框架的依赖,方法是将您的注释包含在注释中:例如 /*@Nullable*/。这很好,因为您可以注释和检查库或共享代码,但仍然可以在另一个不使用框架的项目中使用该库/共享编码。这是一个不错的功能。我已经习惯了使用它,尽管我现在倾向于在我的所有项目中启用 Checker 框架。该框架有一种方法可以通过使用存根文件来注释您使用的尚未注释为空的 API。
我使用 IntelliJ,因为我主要关心 IntelliJ 标记可能产生 NPE 的事物。我同意在 JDK 中没有标准注释令人沮丧。有关于添加它的讨论,它可能会进入 Java 7。在这种情况下,将有更多可供选择!
javax.annotation.Nonnull
被更广泛地接受,不是吗?
根据 Java 7 features list,JSR-308 类型注释被推迟到 Java 8。甚至没有提到 JSR-305 注释。
在最新的 JSR-308 草案的 appendix 中有一些关于 JSR-305 状态的信息。这包括观察到 JSR-305 注释似乎已被放弃。 JSR-305 页面也将其显示为“非活动”。
同时,务实的答案是使用最广泛使用的工具支持的注释类型……并准备好在情况发生变化时更改它们。
事实上,JSR-308 没有定义任何注释类型/类,看起来他们认为它超出了范围。 (考虑到 JSR-305 的存在,他们是对的)。
但是,如果 JSR-308 真的看起来像将其纳入 Java 8,那么如果对 JSR-305 的兴趣重新出现,我也不会感到惊讶。 AFAIK,JSR-305 团队还没有正式放弃他们的工作。他们刚刚安静了2年多。
有趣的是,Bill Pugh(JSR-305 的技术负责人)是 FindBugs 背后的人之一。
对于 Android 项目,您应该使用 android.support.annotation.NonNull
和 android.support.annotation.Nullable
。 Support Library 中提供了这些和其他有用的特定于 Android 的注释。
从 http://tools.android.com/tech-docs/support-annotations:
支持库本身也已使用这些注解进行了注解,因此作为支持库的用户,Android Studio 会根据这些注解检查您的代码并标记潜在问题。
javax.annotation.*
注释的 jsr305
区分静态分析和运行时分析。对内部内容使用静态分析,对代码的公共边界使用运行时分析。
对于不应该为空的东西:
运行时检查:使用“if (x == null) ...”(零依赖)或@javax.validation.NotNull(带有bean验证)或@lombok.NonNull(简单明了)或guava Preconditions.checkNotNull(.. .) 将 Optional 用于方法返回类型(仅)。 Java8 或 Guava。
将 Optional 用于方法返回类型(仅)。 Java8 或 Guava。
静态检查:使用 @NonNull 注释
在适合的地方,在类或包级别使用 @...NonnullByDefault 注释。自己创建这些注释(示例很容易找到)。否则,在方法返回时使用 @...CheckForNull 以避免 NPE
否则,在方法返回时使用 @...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 就有点过头了。
对字段和方法参数使用 Optional 是有争议的(你可以很容易地找到有关它的文章)
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 的 3.1 版),没有办法让 Spotbugs 发出警告。也许 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(编写正确的代码),同时返回有用的错误。
如果有人只是在寻找 IntelliJ 类:您可以从 maven 存储库中获取它们
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>15.0</version>
</dependency>
JSR305 和 FindBugs 是由同一个人编写的。两者都维护得很差,但都是标准的,并且受到所有主要 IDE 的支持。好消息是它们按原样运行良好。
这是默认情况下如何将@Nonnull 应用于所有类、方法和字段的方法。请参阅 https://stackoverflow.com/a/13319541/14731 和 https://stackoverflow.com/a/9256595/14731
定义@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
Eclipse 也有自己的注释。
org.eclipse.jdt.annotation.NonNull
有关详细信息,请参见 http://wiki.eclipse.org/JDT_Core/Null_Analysis。
只是指出 Java Validation API (javax.validation.constraints.*
) 不附带 @Nullable
注释,这在静态分析上下文中非常有价值。这对于运行时 bean 验证是有意义的,因为这是 Java 中任何非原始字段的默认值(即无需验证/强制执行)。出于所述目的,应权衡替代方案。
遗憾的是,JSR 308
不会在此处添加比此项目本地 Not Null 建议更多的值
Java 8
不会附带单个默认注释或它自己的 Checker
框架。与 Find-bugs 或 JSR 305
类似,这个 JSR 由一小部分主要是学术团队维护得很差。
它背后没有商业力量,因此 JSR 308
现在推出 EDR 3
(在 JCP
进行早期草稿审查),而 Java 8
应该在不到 6 个月内发货:-O 顺便说一句,类似于 310
。但与 308 Oracle
不同的是,它现在从其创始人手中接管了这件事,以尽量减少对 Java 平台造成的损害。
Checker Framework
和 JSR 308
背后的每个项目、供应商和学术课程都将创建自己的专有检查器注释。
使源代码在未来几年内不兼容,直到可以找到一些流行的折衷方案并将其添加到 Java 9
或 10
,或者通过 Apache Commons
或 Google Guava
等框架;-)
安卓
这个答案是特定于 Android 的。 Android 具有名为 support-annotations
的支持包。这提供了 dozens of Android specific 注释,还提供了 common ones,如 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) {}
在等待上游整理(Java 8?)时,您还可以定义自己的项目本地 @NotNull
和 @Nullable
注释。这在您使用 Java SE 的情况下也很有用,默认情况下为 javax.validation.constraints
isn't available。
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 {}
诚然,这主要是出于装饰或面向未来的目的,因为上述内容显然本身并没有为这些注释的静态分析添加任何支持。
这里已经有太多答案了,但是 (a) 现在是 2019 年,仍然没有“标准”Nullable
并且 (b) 没有其他答案引用 Kotlin。
对 Kotlin 的引用很重要,因为 Kotlin 与 Java 100% 可互操作,并且它具有核心的 Null 安全特性。在调用 Java 库时,它可以利用这些注释让 Kotlin 工具知道 Java API 是否可以接受或返回 null
。
据我所知,与 Kotlin 兼容的 Nullable
软件包只有 org.jetbrains.annotations
和 android.support.annotation
(现在是 androidx.annotation
)。后者仅与 Android 兼容,因此不能在非 Android JVM/Java/Kotlin 项目中使用。但是,JetBrains 包可以在任何地方使用。
因此,如果您开发的 Java 包也应该在 Android 和 Kotlin 中工作(并且得到 Android Studio 和 IntelliJ 的支持),那么您最好的选择可能是 JetBrains 包。
马文:
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations-java5</artifactId>
<version>15.0</version>
</dependency>
摇篮:
implementation 'org.jetbrains:annotations-java5:15.0'
javax.annotation
、edu.umd.cs.findbugs.annotations
、org.eclipse.jdt.annotation
和 lombok.NonNull
,以及 the implentation also including org.checkerframework
和 io.reactivex.annotations
。
在 Java 8 中还有另一种方法可以做到这一点。我正在做两件事来完成我需要的事情:
通过使用 java.util.Optional 包装可空字段使可空字段显式使用类型使用 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
是一种不好的做法,这里有一个很好的讨论,其中包含利弊Why should Java 8's Optional not be used in arguments
推荐选项,因为在参数中使用 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;}
}
new Role(null,null,null,null);
。使用注释,我的 IDE 和静态分析将警告不能将 null 传递给这些参数。没有它,我直到我运行代码才发现。这就是注释的价值。
description
不为空,并且客户端代码可以传递一个空字符串,但在许多情况下,区分空字符串和没有值可能很方便。谢谢你的评论。我会更新答案。
如果您正在为 android 开发,那么您有点依赖于 Eclipse(编辑:在撰写本文时,不再),它有自己的注释。它包含在 Eclipse 3.8+ (Juno) 中,但默认禁用。
您可以在 Preferences > Java > Compiler > Errors/Warnings > Null analysis(底部的可折叠部分)中启用它。
选中“启用基于注释的空值分析”
http://wiki.eclipse.org/JDT_Core/Null_Analysis#Usage 有关于设置的建议。但是,如果您的工作区中有外部项目(如 facebook SDK),它们可能无法满足这些建议,并且您可能不想在每次 SDK 更新时修复它们;-)
我用:
空指针访问:错误 违反空规范:错误(链接到第 1 点) 潜在的空指针访问:警告(否则 facebook SDK 会有警告)空注释和空推断之间的冲突:警告(链接到第 3 点)
@chaqke
。
如果您使用 Spring Framework 构建您的应用程序,我建议您使用来自 Beans Validation 的 javax.validation.constraints.NotNull
,并打包在以下依赖项中:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
这个注解的主要优点是 Spring 提供了对使用 javax.validation.constraints.NotNull
注解的方法参数和类字段的支持。启用支持所需要做的就是:
提供 api jar 用于 beans 验证和 jar 实现 jsr-303/jsr-349 注释的验证器(Hibernate Validator 5.x 依赖项附带):
例子:
@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
。这里不需要手动工作。
您还可以验证返回值。
为 Beans Validation Framework 提供 javax.validation.constraints.NotNull
的另一个重要好处是,目前它仍在开发中,并且计划在新版本 2.0 中使用新功能。
@Nullable
呢?在 Beans Validation 1.1 中没有类似的东西。好吧,我可以争辩说,如果您决定使用 @NotNull
,那么未使用 @NonNull
注释的所有内容实际上都是“可空的”,因此 @Nullable
注释是无用的。
如果您正在处理一个大型项目,最好创建您自己的 @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
{
}
如果您使用正确的 retention policy,则注释在运行时将不可用。从这个角度来看,它只是一个内部的东西。
尽管这不是一门严格的科学,但我认为使用内部类是最有意义的。
这是一个内部的东西。 (无功能或技术影响)
有很多很多的用法。
IDE 像 IntelliJ 支持自定义 @Nullable/@NotNull 注释。
大多数框架也更喜欢使用自己的内部版本。
其他问题(见评论):
如何在 IntelliJ 中配置它?
点击 IntelliJ 状态栏右下角的“警察”。然后单击弹出窗口中的“配置检查”。下一个 ...
idea
对 test(null);
调用的 void test(@NonNull String s) {}
只字未提
IntelliJ 的优点之一是您不需要使用它们的注释。您可以自己编写,也可以使用您喜欢的任何其他工具。你甚至不限于单一类型。如果您使用的两个库使用不同的 @NotNull
注释,您可以告诉 IntelliJ 使用它们。为此,请转到“配置检查”,单击“恒定条件和例外”检查,然后单击“配置检查”按钮。我尽可能使用 Nullness Checker,因此我将 IntelliJ 设置为使用这些注释,但您可以使其与您想要的任何其他工具一起使用。 (我对其他工具没有意见,因为我多年来一直在使用 IntelliJ 的检查,我喜欢它们。)
太阳现在没有自己的吗?这是什么:
http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Modules-com.sun/istack/com.sun.istack.internal.htm
这似乎与我在过去几年中使用的所有 Java 版本打包在一起。
编辑:正如下面评论中提到的,您可能不想使用这些。在这种情况下,我投票支持 IntelliJ jetbrains 注释!
Spring 5 在包级别有@NonNullApi。对于已经具有 Spring 依赖项的项目,这似乎是一个方便的选择。所有字段、参数和返回值默认为@NonNull 和@Nullable 可以应用在少数不同的地方。
文件包信息.java:
@org.springframework.lang.NonNullApi
package com.acme;
如果您使用的是 Spring 5.x / SpringBoot 2.x,则绝对应该使用 Spring 注释 (org.springframework.lang),因为它们为您提供了带有注释 @NonNullFields
和 @NonNullApi
的默认包范围的 null 检查。您甚至不会与来自其他依赖项的其他 NotNull/NonNull
注释发生冲突,因为您正在使用 @NonNullFields/@NonNullApi
。注释必须在一个名为 package-info.java 的文件中使用,该文件位于包的根目录中:
@NonNullFields
@NonNullApi
package org.company.test;
要从 null 检查中排除某些字段、参数或返回值,只需明确使用 @Nullable
注释。除了使用 @NonNullFields/@NonNullApi
,您还可以在任何地方设置 @NonNull
,但默认情况下使用 @NonNullFields/@NonNullApi
激活空检查可能更好,并且只使用 @Nullable
执行特定异常。
IDE (Intellij) 将突出显示违反 null 条件的代码。如果设置正确,每个开发人员都可以假设字段、参数和返回值不能为空,除非有 @Nullable
注释。有关详细信息,请查看此 article。
另一种选择是 ANTLR 4 提供的注释。在 Pull Request #434 之后,包含 @NotNull
和 @Nullable
注释的工件包括一个注释处理器,如果这些属性之一被滥用,该处理器会产生编译时错误和/或警告(例如,如果两者都应用于同一个项目,或者如果 @Nullable
应用于具有原始类型的项目)。注释处理器在软件开发过程中提供了额外的保证,即这些注释的应用程序所传达的信息是准确的,包括在方法继承的情况下。
JSpecify 现在是要走的路。事实上:their presentation 主动链接到这个问题,并指定他们的目标是让它最终得到一个好的答案。
它有 Android、Guava 和 Kotlin 等主要参与者。
org.jspecify:jspecify:0.2.0
,它实际上有 2 个注释。请参阅github.com/jspecify/jspecify/releases,因为它仍然是测试版,所以他们强调不要在库中使用它。
较新的项目可能应该使用 jakarta.annotation-api(jakarta.annotation
包)。
它从现在开始以只读方式链接 javax.annotation repo,并适合新的 jakarta 生态系统,旨在让社区摆脱所有与 javax
相关的问题。
javax.annotation
的缺点是它 a) 基于死的 JSR,b) 很难找到只提供注释并得到维护的工件。来自 findbugs 的不是:search.maven.org/…javax.annotation
的另一点是它会导致 Java 9 出现问题,因为其他模块也在该包中提供类 (jax-ws)。javax.annotation.NonNull
,但由于其规范负责人擅离职守,因此从未完成。这与甲骨文的任何决定无关。