Вопрос-ответ

Which @NotNull Java annotation should I use?

Какую аннотацию @NotNull Java мне следует использовать?

Я хочу сделать свой код более читабельным, а также использовать инструменты, такие как проверка кода IDE и / или статический анализ кода (FindBugs и Sonar), чтобы избежать исключений NullPointerException. Многие инструменты кажутся несовместимыми с @NotNull/@NonNull/@Nonnull аннотациями друг друга, и перечислять их все в моем коде было бы ужасно читать. Есть предложения, какая из них "лучшая"? Вот список эквивалентных аннотаций, которые я нашел:

Переведено автоматически
Ответ 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), которая используется для реализации средств проверки дефектов, таких как средство проверки на нулевое значение. Я действительно не пробовал никаких других, чтобы предложить какое-либо сравнение, но я был доволен этой реализацией.

Я не связан с группой, предлагающей программное обеспечение, но я фанат.

Четыре вещи, которые мне нравятся в этой системе:


  1. В нем есть средства проверки дефектов на нулевость (@Nullable), но также есть средства проверки на неизменяемость и интернирование (и другие). Я использую первую (nullness) и пытаюсь перейти ко второй (неизменяемость / IGJ). Я пробую третью, но пока не уверен, стоит ли использовать ее в долгосрочной перспективе. Я пока не уверен в общей полезности других средств проверки, но приятно знать, что сам фреймворк представляет собой систему для реализации множества дополнительных аннотаций и средств проверки.


  2. Хорошо работает настройка по умолчанию для проверки на нулевость: ненулевое значение, за исключением локальных значений (NNEL). В основном это означает, что по умолчанию средство проверки обрабатывает все (переменные экземпляра, параметры метода, универсальные типы и т.д.), За исключением локальных переменных, как если бы они по умолчанию имели тип @NonNull . Согласно документации.:



    Значение NNEL по умолчанию приводит к наименьшему количеству явных аннотаций в вашем коде.



    Вы можете установить другое значение по умолчанию для класса или метода, если NNEL у вас не работает.


  3. Этот фреймворк позволяет вам использовать with без создания зависимости от фреймворка путем вложения ваших аннотаций в комментарий: например, /*@Nullable*/. Это приятно, потому что вы можете комментировать и проверять библиотеку или общий код, но при этом иметь возможность использовать эту библиотеку / общий код в другом проекте, который не использует фреймворк. Это приятная функция. Я привык к ее использованию, хотя сейчас я обычно включаю Checker Framework во всех своих проектах.


  4. У фреймворка есть способ аннотировать используемые вами 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.

2023-03-19 11:47 java