我应该使用哪个@NotNull Java注释?
我希望使我的代码更具可读性,并使用IDE代码检查和/或静态代码分析(FindBugs和Sonar)等工具来避免NullPointerException。许多工具似乎彼此不兼容,我应该使用哪个@NotNull Java注释?,java,nullpointerexception,null,annotations,ide,Java,Nullpointerexception,Null,Annotations,Ide,我希望使我的代码更具可读性,并使用IDE代码检查和/或静态代码分析(FindBugs和Sonar)等工具来避免NullPointerException。许多工具似乎彼此不兼容,@NotNull/@NonNull/@NonNull注释并在我的代码中列出所有这些工具,阅读起来会很糟糕。哪一个是“最好的”有什么建议吗?以下是我找到的等效注释列表: javax.validation.constraints.NotNull 为运行时验证而不是静态分析而创建。 edu.umd.cs.findbugs.
@NotNull
/@NonNull
/@NonNull
注释并在我的代码中列出所有这些工具,阅读起来会很糟糕。哪一个是“最好的”有什么建议吗?以下是我找到的等效注释列表:
javax.validation.constraints.NotNull
为运行时验证而不是静态分析而创建。
edu.umd.cs.findbugs.annotations.NonNull
用于()及其后续静态分析,因此用于声纳(现在)
,javax.annotation.Nonnull
这可能也适用于FindBugs,但处于非活动状态。(另见:)org.jetbrains.annotations.NotNull
IntelliJ IDEA用于静态分析。
lombok.NonNull
用于控制中的代码生成。
占位符注释,因为没有标准。
,android.support.annotation.NonNull
Android中提供的标记注释,由support annotations package提供
org.eclipse.jdt.annotation.NonNull
Eclipse用于静态代码分析
事实上,JSR-308没有定义任何注释类型/类,看起来他们认为它超出了范围。(考虑到JSR-305的存在,他们是对的) 然而,如果JSR-308看起来真的像是要进入Java8,那么如果对JSR-305的兴趣恢复的话,我也不会感到惊讶。好了,JSR-305团队还没有正式放弃他们的工作。他们已经安静了2年多了
有趣的是,Bill Pugh(JSR-305的技术负责人)是FindBugs背后的人之一。sun现在不是有自己的吗?这是什么:
这似乎与我在过去几年中使用的所有Java版本一起打包 编辑:如下面的评论所述,您可能不想使用这些。在这种情况下,我投票支持IntelliJ jetbrains 我非常喜欢,它是类型注释()的一个实现,用于实现缺陷检查器,如空值检查器。我还没有真正尝试过任何其他方法来提供任何比较,但是我对这个实现很满意
package lombok;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NonNull {}
我不是提供软件的团体的成员,但我是一个粉丝
我喜欢这个系统的四个方面:
/*@Nullable*/
。这很好,因为您可以注释和检查库或共享代码,但仍然能够在不使用框架的另一个项目中使用该库/共享代码。这是一个很好的特性。我已经习惯于使用它,尽管我现在倾向于在我所有的项目上启用Checker框架在等待上游(Java8?)对此进行分类时,您还可以定义自己的项目本地
@NotNull
和@Nullable
注释。这在使用JavaSE时也很有用,默认情况下,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 {}
不可否认,这主要是为了装饰或未来验证的目的,因为上述内容本身显然没有为这些注释的静态分析添加任何支持。只是指出Java验证API(
javax.Validation.constraints.*
)没有提供@Nullable
注释,这在静态分析环境中非常有价值。这对于运行时bean验证是有意义的,因为这是Java中任何非原语字段的默认值(即
org.eclipse.jdt.annotation.NonNull
@NotNullByDefault
package com.example.foo;
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>15.0</version>
</dependency>
compile 'com.android.support:support-annotations:23.1.1'
import android.support.annotation.NonNull;
void foobar(@NonNull Foo bar) {}
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);
}
//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();
}
// 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);
}
}
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);
}
@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
{
}
public @interface NonNull {}
public @interface Nullable {}
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
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 {}
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 {}
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();
}
}
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</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>
@Configuration
@ValidationConfig
public class ValidationConfig implements MyService {
@Bean
public MethodValidationPostProcessor providePostProcessor() {
return new MethodValidationPostProcessor()
}
}
@Service
@Validated
public class MyServiceImpl implements MyService {
@Override
public Something doSomething(@NotNull String myParameter) {
// No need to do something like assert myParameter != null
}
}
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations-java5</artifactId>
<version>15.0</version>
</dependency>
implementation 'org.jetbrains:annotations-java5:15.0'
@org.springframework.lang.NonNullApi
package com.acme;