Какую аннотацию @NotNull Java следует использовать? - PullRequest
869 голосов
/ 11 февраля 2011

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

Ответы [ 21 ]

127 голосов
/ 09 марта 2017

Поскольку Oracle решил не стандартизировать @NonNull (и @Nullable) на данный момент, я боюсь, что нет хорошего ответа.Все, что мы можем сделать, это найти прагматическое решение, и мое следующее:

Синтаксис

С чисто стилистической точки зрения я хотел бы избежать любых ссылок на IDE, Framework или любой инструментарий, кроме 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)).

Как описано в в этом ответе SO влияние аннотаций времени выполнения меньше, чем можно подумать, но они имеют преимущество, заключающееся в том, что инструменты позволяют выполнять проверки во время выполнения в дополнение квремя компиляции.

Другим важным отличием является , где в коде могут использоваться аннотации.Есть два разных подхода.Некоторые пакеты используют контексты стиля JLS 9.6.4.1.В следующей таблице представлен обзор:


                                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

в этом раунде.

Код

Чтобы помочь вам самостоятельно сравнить дальнейшие детали, я перечислю код каждой аннотации ниже.Чтобы упростить сравнение, я удалил комментарии, импорт и аннотацию @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, поэтому я перечислю их отдельно У Ломбока довольно скучный @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();
    }
}

Поддержка

По моему опыту, Javax.annotation по крайней мере поддерживается Eclipse и Checker Framework из коробки.

Основная информация

Моей идеальной аннотацией был бы синтаксис java.annotation с реализацией Checker Framework.

Если вы не собираетесь использовать Checker Framework, javax.annotation ( JSR-305 ) по-прежнему остается лучшим выбором на данный момент.

Если вы хотите купить в Checker Framework, просто используйте их org.checkerframework.checker.nullness.qual.


Источники

  • android.support.annotation от android-5.1.1_r1.jar
  • edu.umd.cs.findbugs.annotations from findbugs-annotations-1.0.0.jar
  • org.eclipse.jdt.annotation от org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
  • org.jetbrains.nnotations из 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
86 голосов
/ 11 февраля 2011

Мне очень нравится Checker Framework , который представляет собой реализацию аннотаций типов ( JSR-308 ), которая используется для реализации средств проверки дефектов, таких как средство проверки на нулевое значение.На самом деле я не пробовал никого другого, чтобы предложить какое-либо сравнение, но я доволен этой реализацией.

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

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

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

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

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

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

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

  4. Фреймворк имеет способ аннотировать API , которые вы используете, которые еще не аннотированы для нулевого значения с помощью заглушек.

52 голосов
/ 11 февраля 2011

Я использую IntelliJ, потому что в основном меня интересует, как IntelliJ помечает вещи, которые могут создавать NPE.Я согласен, что расстраивает отсутствие стандартной аннотации в JDK.Говорят о его добавлении, он может превратиться в Java 7. В таком случае будет еще один на выбор!

32 голосов
/ 11 февраля 2011

Согласно списку возможностей Java 7 Примечания типа JSR-308 откладываются до Java 8. Примечания JSR-305 даже не упоминаются.

Существует немного информации о состоянии JSR-305 в приложении к последней версии JSR-308. Это включает в себя наблюдение, что аннотации JSR-305 кажутся заброшенными. Страница JSR-305 также показывает его как «неактивный».

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


Фактически, JSR-308 не определяет какие-либо типы / классы аннотаций, и похоже, что они думают, что это выходит за рамки. (И они правы, учитывая существование JSR-305).

Однако, если JSR-308 действительно выглядит как превращение в Java 8, меня не удивит, если возродится интерес к JSR-305. AFAIK, команда JSR-305 официально не отказалась от своей работы. Они просто молчали 2+ года.

Интересно, что Билл Пью (технический руководитель JSR-305) - один из тех, кто стоит за FindBugs.

26 голосов
/ 05 сентября 2014

Для проектов Android вы должны использовать android.support.annotation.NonNull и android.support.annotation.Nullable. Эти и другие полезные аннотации для Android доступны в библиотеке поддержки .

С http://tools.android.com/tech-docs/support-annotations:

Сама библиотека поддержки также помечена этими аннотации, так как пользователь библиотеки поддержки, Android Studio будет уже проверьте свой код и пометьте потенциальные проблемы на основе этих аннотаций.

18 голосов
/ 25 апреля 2014

Если кто-то просто ищет классы IntelliJ: вы можете получить их из репозитория maven с помощью

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations</artifactId>
    <version>15.0</version>
</dependency> 
18 голосов
/ 23 ноября 2012

JSR305 и FindBugs созданы одним и тем же человеком. Оба плохо обслуживаются, но являются стандартными и поддерживаются всеми основными IDE. Хорошей новостью является то, что они работают хорошо, как есть.

Вот как по умолчанию применять @Nonnull ко всем классам, методам и полям. См https://stackoverflow.com/a/13319541/14731 и https://stackoverflow.com/a/9256595/14731

  1. Определить @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;

ОБНОВЛЕНИЕ : По состоянию на 12 декабря 2012 года JSR 305 указан как «Неактивный». Согласно документации:

JSR, который был признан Исполнительным комитетом "спящим", или тот, который достиг конца своей естественной жизни.

Похоже, что JSR 308 превращает его в JDK 8, и хотя JSR не определяет @NotNull, сопровождающий Checkers Framework делает. На момент написания этой статьи плагин Maven не работал из-за этой ошибки: https://github.com/typetools/checker-framework/issues/183

12 голосов
/ 17 сентября 2011

Eclipse также имеет свои собственные аннотации.

org.eclipse.jdt.annotation.NonNull

Подробнее см. http://wiki.eclipse.org/JDT_Core/Null_Analysis.

11 голосов
/ 01 августа 2011

Просто отметив, что Java Validation API (javax.validation.constraints.*) не поставляется с аннотацией @Nullable, что очень ценно в контексте статического анализа.Это имеет смысл для проверки bean-компонента во время выполнения, поскольку это значение по умолчанию для любого не примитивного поля в Java (т. Е. Ничего для проверки / применения).Для заявленных целей следует взвесить альтернативы.

7 голосов
/ 10 октября 2015

Android

Этот ответ зависит от Android.Android имеет пакет поддержки под названием support-annotations.Это обеспечивает десятки из специфичных для Android аннотаций, а также предоставляет общих подобных NonNull, Nullable и т. Д.

Для добавления пакет поддержки-аннотации , добавьте следующую зависимость в ваш build.gradle:

compile 'com.android.support:support-annotations:23.1.1'

и затем используйте:

import android.support.annotation.NonNull;

void foobar(@NonNull Foo bar) {}
...