Какую аннотацию @NotNull Java мне следует использовать?
Я хочу сделать свой код более читабельным, а также использовать инструменты, такие как проверка кода IDE и / или статический анализ кода (FindBugs и Sonar), чтобы избежать исключений NullPointerException. Многие инструменты кажутся несовместимыми с @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
Используется IDE IntelliJ IDEA для статического анализа.
Документация
lombok.NonNull
Используется для управления генерацией кода в Project Lombok.
Аннотация-заполнитель, поскольку стандарта нет.
исходный код, документация
androidx.annotation.NonNull
Аннотация маркера доступна в Android, предоставляется пакетом аннотаций
Документация
org.eclipse.jdt.annotation.NonNull
Используется Eclipse для статического анализа кода
Документация
Переведено автоматически
Ответ 1
Поскольку 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 был бы шагом в правильном направлении.
Я надеялся, что все они имеют в основном одну и ту же тривиальную реализацию, но подробный анализ показал, что это неправда.
First for the similarities:
The @NonNull
annotations all have the line
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)
).
Как описано в этом ответе SO, влияние аннотаций во время выполнения меньше, чем можно было бы подумать, но их преимущество заключается в том, что они позволяют инструментам выполнять проверки во время выполнения в дополнение к проверкам во время компиляции.
Еще одно важное отличие заключается в том, где в коде можно использовать аннотации. Существуют два разных подхода. Некоторые пакеты используют контексты в стиле 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.annotation
, javax.annotation
и org.checkerframework.checker.nullness.qual
используйте контексты, определенные в JLS 4.11, что, на мой взгляд, является правильным способом сделать это.
Это оставляет нас с
javax.annotation
org.checkerframework.checker.nullness.qual
в этом раунде.
Чтобы помочь вам сравнить дополнительные детали самостоятельно, я перечисляю код каждой аннотации ниже. Чтобы упростить сравнение, я удалил комментарии, импорт и @Documented
аннотацию. (они все были, @Documented
за исключением классов из пакета Android). Я изменил порядок строк и @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
the @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 и фреймворком проверки "из коробки".
Моей идеальной аннотацией был бы java.annotation
синтаксис с реализацией Checker Framework.
Если вы не собираетесь использовать фреймворк проверки, то javax.annotation
(JSR-305) по-прежнему является вашим лучшим выбором на данный момент.
Если вы готовы приобрести фреймворк Checker, просто используйте их org.checkerframework.checker.nullness.qual
.
android.support.annotation
От android-5.1.1_r1.jar
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
javax.annotation
От gwt-dev-2.5.1-sources.jar
org.checkerframework.checker.nullness.qual
От checker-framework-2.1.9.zip
lombok
из lombok
commit f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
javax.validation.constraints
От validation-api-1.0.0.GA-sources.jar
Ответ 2
Мне очень нравится Checker Framework, который представляет собой реализацию аннотаций типов (JSR-308), которая используется для реализации средств проверки дефектов, таких как средство проверки на нулевое значение. Я действительно не пробовал никаких других, чтобы предложить какое-либо сравнение, но я был доволен этой реализацией.
Я не связан с группой, предлагающей программное обеспечение, но я фанат.
Четыре вещи, которые мне нравятся в этой системе:
В нем есть средства проверки дефектов на нулевость (@Nullable), но также есть средства проверки на неизменяемость и интернирование (и другие). Я использую первую (nullness) и пытаюсь перейти ко второй (неизменяемость / IGJ). Я пробую третью, но пока не уверен, стоит ли использовать ее в долгосрочной перспективе. Я пока не уверен в общей полезности других средств проверки, но приятно знать, что сам фреймворк представляет собой систему для реализации множества дополнительных аннотаций и средств проверки.
Хорошо работает настройка по умолчанию для проверки на нулевость: ненулевое значение, за исключением локальных значений (NNEL). В основном это означает, что по умолчанию средство проверки обрабатывает все (переменные экземпляра, параметры метода, универсальные типы и т.д.), За исключением локальных переменных, как если бы они по умолчанию имели тип @NonNull . Согласно документации.:
Значение NNEL по умолчанию приводит к наименьшему количеству явных аннотаций в вашем коде.
Вы можете установить другое значение по умолчанию для класса или метода, если NNEL у вас не работает.
Этот фреймворк позволяет вам использовать with без создания зависимости от фреймворка путем вложения ваших аннотаций в комментарий: например, /*@Nullable*/
. Это приятно, потому что вы можете комментировать и проверять библиотеку или общий код, но при этом иметь возможность использовать эту библиотеку / общий код в другом проекте, который не использует фреймворк. Это приятная функция. Я привык к ее использованию, хотя сейчас я обычно включаю Checker Framework во всех своих проектах.
У фреймворка есть способ аннотировать используемые вами API, которые еще не аннотированы на предмет недействительности, с помощью файлов-заглушек.
Ответ 3
Я использую аннотацию IntelliJ, потому что меня больше всего волнует, что IntelliJ помечает вещи, которые могут привести к NPE. Я согласен, что расстраивает отсутствие стандартной аннотации в JDK. Говорят о ее добавлении, возможно, она попадет в Java 7. В этом случае будет еще одна на выбор!
Ответ 4
Согласно списку функций Java 7 аннотации типа JSR-308 переносятся на Java 8. Аннотации типа JSR-305 даже не упоминаются.
В приложении к последнему проекту JSR-308 есть немного информации о состоянии JSR-305. Сюда входит замечание о том, что аннотации JSR-305, похоже, заброшены. Страница JSR-305 также показывает ее как "неактивную".
Между тем, прагматичный ответ заключается в использовании типов аннотаций, которые поддерживаются наиболее широко используемыми инструментами ... и будьте готовы изменить их, если ситуация изменится.
Фактически, JSR-308 не определяет никаких типов / классов аннотаций, и, похоже, они думают, что это выходит за рамки. (И они правы, учитывая существование JSR-305).
Однако, если JSR-308 действительно выглядит так, как будто она включена в Java 8, меня не удивит, если интерес к JSR-305 возродится. AFAIK, команда JSR-305 формально не прекращала свою работу. Они просто молчали более 2 лет.
Интересно, что Билл Пью (технический руководитель JSR-305) является одним из разработчиков FindBugs.