Which @NotNull Java annotation should I use?
我希望使我的代码更可读,并使用诸如IDE代码检查和/或静态代码分析(findbugs和sonar)之类的工具来避免nullpointerException。许多工具似乎与彼此的
javax.validation.constraints.NotNull 创建用于运行时验证,而不是静态分析。文档edu.umd.cs.findbugs.annotations.NonNull 用于findbugs静态分析,因此是声纳(现在是声纳管)文档javax.annotation.Nonnull 这可能也适用于findbugs,但JSR-305是非活动的。(另请参见:JSR 305的状态是什么?)来源org.jetbrains.annotations.NotNull Intellij IDEA IDE用于静态分析。文档lombok.NonNull 用于控制Lombok项目中的代码生成。占位符注释,因为没有标准。源,文档android.support.annotation.NonNull Android中提供的标记注释,由支持注释包提供文档org.eclipse.jdt.annotation.NonNull Eclipse用于静态代码分析文档
由于Oracle目前决定不标准化@nonnull(和@nullable),恐怕没有好的答案。我们所能做的就是找到一个实用的解决方案,我的方法如下:好的。句法
从纯风格的观点来看,我希望避免引用IDE、框架或任何工具包,除了Java本身。好的。
这就排除了:好的。
- android.support.annotation(安卓支持注释)
- edu.umd.cs.findbugs.annotations.注释
- org.eclipse.jdt.annotation注释
- org.jetbrains.annotations.注释
- org.checkerframework.checker.nullness.qual
- LoBOBK.NONLULL
这会给我们留下javax.validation.constraints或javax.annotation。前者和jee一起。如果这比javax.annotation更好的话,这将是一个有争议的问题,而javax.annotation最终可能会随JSE一起出现,或者根本不会出现。我个人更喜欢javax.annotation,因为我不喜欢jee依赖。好的。
这让我们好的。
javax.注释好的。
这也是最短的一个。好的。
只有一种语法更好:java.annotation.nullable。随着其他包裹的逐渐增多从过去的Javax到Java,JavaX.注释将朝着正确的方向迈出一步。好的。实施
我希望它们都有基本相同的小实现,但一份详细的分析表明这不是真的。好的。
首先是相似之处:好的。
@nonnull注释都有行好的。
1 | public @interface NonNull {} |
除了好的。
- org.jetbrains.annotations,它调用@notnull并有一个简单的实现
- javax.annotation实现更长
- javax.validation.constraints,它还调用它@notnull并有一个实现
@nullable注释都有行好的。
1 | public @interface Nullable {} |
除了(再次)org.jetbrains.annotations及其简单的实现。好的。
对于差异:好的。
最引人注目的是好的。
- javax.注释
- 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 | FIELD METHOD PARAMETER LOCAL_VARIABLE android.support.annotation X X X edu.umd.cs.findbugs.annotations X X X X org.jetbrains.annotation X X X X lombok X X X X javax.validation.constraints X X X |
org.eclipse.jdt.annotation、javax.annotation和org.checkerFramework.checker.nullness.qual使用中定义的上下文JLS 4.11,我认为这是正确的方法。好的。
这让我们好的。
- javax.annotation
- org.checkerframework.checker.nullness.qual
在这轮。
代码
帮助你自己比较进一步的细节在下面的代码的注释列表。让我比较容易被删除的评论和注释,进口记录"。(他们都有"类的研究除了从Android包)。我的目标reordered线场和标准化"和"资格。
1 2 3 4 | package android.support.annotation; @Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) public @interface NonNull {} |
1 2 3 4 | package edu.umd.cs.findbugs.annotations; @Retention(CLASS) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE}) public @interface NonNull {} |
1 2 3 4 | package org.eclipse.jdt.annotation; @Retention(CLASS) @Target({ TYPE_USE }) public @interface NonNull {} |
1 2 3 4 | 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 | 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 | 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 {} |
这里是"完整的"将实现:
1 2 3 4 | package android.support.annotation; @Retention(CLASS) @Target({METHOD, PARAMETER, FIELD}) public @interface Nullable {} |
1 2 3 4 | package edu.umd.cs.findbugs.annotations; @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE}) @Retention(CLASS) public @interface Nullable {} |
1 2 3 4 | package org.eclipse.jdt.annotation; @Retention(CLASS) @Target({ TYPE_USE }) public @interface Nullable {} |
1 2 3 4 | package org.jetbrains.annotations; @Retention(CLASS) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE}) public @interface Nullable {String value() default"";} |
1 2 3 4 5 | package javax.annotation; @TypeQualifierNickname @Nonnull(when = When.UNKNOWN) @Retention(RUNTIME) public @interface Nullable {} |
1 2 3 4 5 6 7 8 9 10 | 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 {} |
下面的两个软件包没有"可空,所以他们的菜单列表龙目岛有一个非空的很无聊"。在javax.validation.constraints"实际上是"非空"notnull a它有一longish实现。
1 2 3 4 | 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 | 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和格子的盒子。
摘要
我的理想是java.annotation注释的语法与格子框架的实现。
如果你不打算使用的格子框架javax.annotation(JSR - 305)仍然是你最好的赌注是对的时间。
如果你愿意购买到格子框架只是使用他们的org.checkerframework.checker.nullness.qual。
来源
- 从Android android.support.annotation 5.1.1 _ r1.jar
- 从findbugs-annotations-1.0.0.jar edu.umd.cs.findbugs.annotations
- 从org.eclipse.jdt.annotation _ 2.1.0.v20160418-1457.jar org.eclipse.jdt.annotation
- 从jetbrains-annotations-13.0.jar org.jetbrains.annotations
- 从gwt-dev-2.5.1-sources.jar javax.annotation
- 从checker-framework-2.1.9.zip org.checkerframework.checker.nullness.qual
- 龙目岛和龙目岛提交f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
- 从validation-api-1.0.0.ga-sources.jar javax.validation.constraints
好的。
我非常喜欢checker框架,它是类型注释(JSR-308)的一个实现,用于实现缺陷检查程序,比如nullness检查程序。我没有尝试过其他人提供任何比较,但我对这个实现很满意。
我不属于提供软件的小组,但我是一个粉丝。
我喜欢这个系统的四件事:
它有一个用于空性(@nullable)的缺陷检查程序,但也有一个用于不可变性和中间性(以及其他)的缺陷检查程序。我使用第一个(空值),并且尝试使用第二个(不可变/igj)。我正在尝试第三个,但我还不确定是否要长期使用它。我还不相信其他检查程序的一般用途,但是很高兴知道框架本身是一个实现各种附加注释和检查程序的系统。
空度检查的默认设置工作良好:非空,除了局部变量(nnel)。基本上,这意味着默认情况下,检查器将除局部变量之外的所有变量(实例变量、方法参数、泛型类型等)视为默认的@nonnull类型。根据文件:
The NNEL default leads to the smallest number of explicit annotations in your code.
如果NNEL不适合您,您可以为类或方法设置不同的默认值。
此框架允许您在不创建对框架的依赖性的情况下使用,方法是将注释括在注释中:例如
框架有一种方法来注释您使用的API,这些API还没有通过使用存根文件为空进行注释。
我使用Intellij,因为我主要关注Intellij标记可能产生NPE的事物。我同意在JDK中没有标准注释是令人沮丧的。有人说添加它,它可能会进入Java 7。在这种情况下,还有一个选择!
根据Java 7特性列表,JSR308类型注解被推迟到Java 8。甚至没有提到JSR-305注释。
在最新的JSR-308草案的附录中有一些关于JSR-305状态的信息。这包括观察到JSR-305注释似乎被放弃了。JSR-305页面还将其显示为"非活动"。
同时,语用的答案是使用最广泛使用的工具支持的注释类型…如果情况发生变化,要做好改变的准备。
事实上,JSR-308没有定义任何注释类型/类,看起来它们认为它超出了范围。(考虑到JSR-305的存在,他们是对的)。
但是,如果JSR308看起来像是进入Java 8,如果JSR305的兴趣恢复了,我也不会感到惊讶。阿法克,JSR-305小组还没有正式放弃他们的工作。他们已经安静了2年多了。
有趣的是,Bill Pugh(JSR-305的技术负责人)是findbugs背后的人之一。
对于机器人项目,你应该使用
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.
如果任何人只是在寻找智力阶级:你可以带他们去圣母院休息。
1 2 3 4 5 | <dependency> <groupId>org.jetbrains</groupId> annotations</artifactId> <version>15.0</version> </dependency> |
JSR305和FindBugs由同一个人编写。两者都维护得很差,但都是标准的,并且由所有主要的IDE支持。好消息是他们工作得很好。
以下是默认情况下如何将@nonnull应用于所有类、方法和字段。请参阅https://stackoverflow.com/a/13319541/14731和https://stackoverflow.com/a/9256595/14731
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | 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。在每个包中添加注释:
1 2 | @NotNullByDefault package com.example.foo; |
更新:截至2012年12月12日,JSR 305被列为"休眠"。根据文件:
A JSR that was voted as"dormant" by the Executive Committee, or one that has reached the end of its natural lifespan.
看起来JSR 308正在将其转换成JDK 8,虽然JSR没有定义@notnull,但附带的
Eclipse也有自己的注释。
1 | org.eclipse.jdt.annotation.NonNull |
有关详细信息,请参阅http://wiki.eclipse.org/jdt_core/null_analysis。
只需指出Java验证API(EDCOX1,3×)不带有EDCOX1,1注释,这在静态分析上下文中是非常有价值的。运行时bean验证是有意义的,因为这是Java中任何非原始字段的默认(即没有验证/强制执行)。出于所述目的,应权衡备选方案。
不幸的是,
因此,
每个项目、供应商和学术类,如
使源代码在未来几年内不兼容,直到找到一些流行的折衷方案,并可能添加到
仙女座
这个答案是安卓特异的。安德罗德支持包装称为EDOCX1。这是安卓特异性注释的一部分,也提供了类似于EDOCX1的共同点。
To add support-annotations package,add the following dependency in your build.gradle:
ZZU1
然后使用:
1 2 3 | import android.support.annotation.NonNull; void foobar(@NonNull Foo bar) {} |
区分静态分析和运行时分析。对内部内容使用静态分析,对代码的公共边界使用运行时分析。
对于不应为空的内容:
运行时检查:使用"if(x==null)…"(零依赖项)或@javax.validation.notnull(带bean验证)或@lombok.nonnull(简单明了)或guavas preconditions.checknotnull(…)
- 对方法返回类型使用可选(仅限)。Java8或Guava。
静态检查:使用@nonnull注释
- 在合适的地方,在类或包级别上使用@…非空的默认注释。自己创建这些注释(很容易找到示例)。
- 否则,在方法返回时使用@…checkfornull以避免NPE
这将产生最好的结果:IDE中的警告、findbugs和checkerframework的错误、有意义的运行时异常。
不要期望静态检查成熟,它们的命名没有标准化,不同的库和IDE对它们的处理不同,忽略它们。jsr305 javax.annotations.*类看起来像标准的,但它们不是标准的,它们会导致使用java9+拆分包。
一些注释说明:
- findbugs/spotbugs/jsr305注释与包javax.validation.*与java9+中的其他模块冲突,也可能违反Oracle许可证
- spotbugs注释仍然依赖于编译时的jsr305/findbugs注释(在编写https://github.com/spotbugs/spotbugs/issues/421时)
- jetbrains@notnull名称与@javax.validation.notnull冲突。
- 与javax.annotations相比,用于静态检查的JetBrains、Eclipse或CheckersFramework注释具有优势,即它们不会与java9及更高版本中的其他模块冲突。
- @nullable并不意味着查找你(或你的IDE)认为它意味着什么。findbugs将忽略它(在成员上)。悲伤,但真实(https://sourceforge.net/p/findbugs/bugs/1181)
- 对于一个IDE外部的静态检查,存在两个自由工具:spotbugs(以前是findbugs)和checkersframework。
- Eclipse库默认为@nonNullBy,JSR305仅默认为@ParametersAreonNullBy。这些仅仅是将基本注释应用于包(或类)中的所有内容的方便包装器,您可以轻松地创建自己的注释。这可用于包装。这可能与生成的代码(例如Lombok)冲突。
- EclipseJDT注释不适用于静态方法返回和其他一些情况。
- 对于与其他人共享的库,应避免使用lombok作为导出依赖项,传递依赖项越少越好。
- 使用bean验证框架是强大的,但是需要很高的开销,所以这是为了避免手动的空检查而造成的。
- 对字段和方法参数使用可选是有争议的(您可以很容易地找到有关它的文章)
- android空注释是android支持库的一部分,它们与许多其他类一起提供,并且不能很好地与其他注释/工具一起使用。
在Java9之前,这是我的建议:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | // file: package-info.java @javax.annotation.ParametersAreNonnullByDefault package example; // file: PublicApi package example; public class PublicApi { /** * @param firstname MUST NOT be null * @param lastname MUST NOT be null */ public Person createPerson( // Spotbugs ignores the param annotations, but IDEs will show problems @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发出警告。也许棋盘架可以做到。
在等待它被排序的上游(Java 8?)您还可以定义自己的项目本地
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 {} |
诚然,这在很大程度上是为了装饰或将来的校对目的,因为上面显然没有添加对这些注释的静态分析的任何支持。
如果您是为Android开发的,那么您与Eclipse有点关联(编辑:在编写时,不再是这样),Eclipse有自己的注释。它包含在Eclipse3.8+中(juno),但默认情况下是禁用的。
您可以在首选项> java>编译器>错误/警告> NULL分析(底部可折叠部分)启用它。
选中"启用基于批注的空分析"
http://wiki.eclipse.org/jdt_-core/null_-analysis用法对设置有建议。但是,如果您的工作区中有外部项目(如Facebook SDK),它们可能不满足这些建议,并且您可能不想在每次更新SDK时修复它们;-)
我使用:
如果您正在处理一个大型项目,那么最好创建自己的
例如:
1 2 3 4 5 6 7 8 9 | @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 { } |
如果使用正确的保留策略,则批注在运行时将不可用。从这个角度来看,它只是一个内在的东西。
尽管这不是一门严格的科学,但我认为使用一个内部类是最有意义的。
- 这是一个内在的东西。(无功能或技术影响)
- 有许多用法。
- 类似于ide的intellij支持自定义的
@Nullable /@NotNull 注释。 - 大多数框架也喜欢使用自己的内部版本。
其他问题(见评论):
如何在Intellij中配置?
Click the"police officer" in the lower right corner of the IntelliJ status bar. And click"Configure inspections" in the popup. Next ...
在Java 8号有另一条路可以做这件事。我正在做两件事来实现我所需要的
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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); } |
所以我的问题是,当我们使用爪哇8时,我们是否需要注释?
编辑:我发现有人认为使用
太阳现在没有自己的了吗?这是什么:http://www.java2s.com/open-source/java-document/6.0-jdk-modules-com.sun/istack/com.sun.istack.internal.htm
这似乎是包装在过去几年里我使用的所有版本的Java。
编辑:正如下面的评论中提到的,您可能不想使用这些。在这种情况下,我投票赞成IntellijJetBrains注释!
如果您使用Spring框架构建应用程序,我建议您使用
1 2 3 4 5 | <dependency> <groupId>javax.validation</groupId> validation-api</artifactId> <version>1.1.0.Final</version> </dependency> |
这个注释的主要优点是Spring同时支持方法参数和用
提供用于beans验证的api jar和带有jsr-303/jsr-349注释验证程序实现的jar(与hibernate validator 5.x依赖项一起提供):
1 2 3 4 5 6 7 8 9 10 | <dependency> <groupId>javax.validation</groupId> validation-api</artifactId> <version>1.1.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> hibernate-validator</artifactId> <version>5.4.1.Final</version> </dependency> |
为Spring上下文提供方法验证后处理器
1 2 3 4 5 6 7 8 9 | @Configuration @ValidationConfig public class ValidationConfig implements MyService { @Bean public MethodValidationPostProcessor providePostProcessor() { return new MethodValidationPostProcessor() } } |
最后,使用Spring的
例子:
1 2 3 4 5 6 7 8 9 | @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)将抛出
您还可以验证返回值。
那么
关于intellij的一个好处是不需要使用它们的注释。你可以自己写,也可以使用你喜欢的其他工具。你甚至不局限于一种类型。如果您使用的两个库使用不同的@notnull注释,则可以告诉intellij同时使用这两个注释。为此,请转到"配置检查",单击"恒定条件和例外"检查,然后单击"配置检查"按钮。我尽可能地使用空度检查器,所以我设置了Intellij来使用这些注释,但是您可以使用任何其他工具。(我对其他工具没有意见,因为我多年来一直在使用Intellij的检查,我喜欢它们。)
另一个选择是与Antlr 4一起提供说明。跟踪脉冲请求;434,the artifact containing the EDOCX1 0 x1 x1 x1 x1 x1 x1 \ x1 \\\\\\ x1 \\\\\ x1注释处理器在软件开发过程中提供额外的保证,通过应用注释来传输信息,包括在方法继承的案例中。