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

1001

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

  • javax.validation.constraints.NotNull
    Создан для проверки во время выполнения, а не для статического анализа.
    документация

  • edu.umd.cs.findbugs.annotations.NonNull
    Используется FindBugs статического анализа и , следовательно , эхолота (теперь Sonarqube )
    документации

  • javax.annotation.Nonnull
    Это может работать и с Findbugs, но JSR-305 неактивен. (См. Также: Каков статус JSR 305? ) Источник

  • org.jetbrains.annotations.NotNull
    Используется IntelliJ IDEA IDE для статического анализа.
    документация

  • lombok.NonNull
    Используется для управления генерацией кода в Project Lombok .
    Заполнение аннотации, так как нет стандарта.
    источник , документация

  • android.support.annotation.NonNull
    Аннотация для маркеров, доступная в Android, предоставляется документацией пакета support-annotations

  • org.eclipse.jdt.annotation.NonNull
    Используется Eclipse , для анализа статического кода
    документации

jaxzin
источник
203
Apache должен изобрести «общую» аннотацию и инструмент, который может преобразовать ее в любую другую аннотацию. Решение проблемы слишком большого числа стандартов состоит в том, чтобы изобрести новый стандарт.
неопровержимый
6
@irreputable если apache изобрел новый «общий», его будет 56 версий, совпадающих с другими проектами. И, в любом случае, это не будет стандартом (стандартным! = Широко распространенным). Лучше использовать что-нибудь действительно стандартное, javax?. *. Кстати, в этих примерах не слишком много стандартов, я просто вижу 1 или 2.
ymajoros
6
javax.annotation.Nonnull работает с findbugs (только что проверил), что является убедительной причиной для его использования.
Николас С
20
Если я просто напишу @NotNull, это относится к com.sun.istack.internal.NotNull. OMG ...
Томас Веллер
3
@MozartBrocchini - дополнительные функции полезны в тех случаях, когда вы могли ранее использовать NullObjects. Они на самом деле не имеют той же цели, что и аннотация runtime \ @NotNull, и вводят утомительную распаковку.
Дейв

Ответы:

206

Начиная с 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 было бы шагом в правильном направлении.

Реализация

Я надеялся, что у них у всех одинаковая тривиальная реализация, но подробный анализ показал, что это не так.

Сначала за сходство:

Все @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. Следующая таблица дает обзор:

                                ПАРАМЕТР ПОЛЯ МЕТОДА LOCAL_VARIABLE 
android.support.annotation XXX   
edu.umd.cs.findbugs.annotations XXXX
org.jetbrains.annotation XXXX
Ломбок ХХХХ
javax.validation.constraints XXX   

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 от 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коммитаf6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints от validation-api-1.0.0.GA-sources.jar
Людвиг Вайнцерль
источник
7
Недостатком javax.annotationявляется то, что он а) основан на мертвой JSR, б) трудно найти артефакт, который просто предоставляет аннотации и поддерживается. Один из findbugs не является: search.maven.org/…
robinst
18
Еще одно замечание javax.annotation- это вызывает проблемы с Java 9, потому что другие модули также предоставляют классы в этом пакете (jax-ws).
Робинст
10
@kevinarpe: проект Findbugs мертв, и проект-наследник Spotbugs удаляет эти аннотации: github.com/spotbugs/spotbugs/pull/180
robinst
4
JSR 305 , который был бы стандартизирован javax.annotation.NonNull, так и не был завершен, потому что его спецификационный лидер вышел из AWOL Это не имело никакого отношения к решению Oracle.
Марк Рейнхольд
5
Еще одна причина не использовать jsr305.jar заключается в том, что он явно нарушает двоичную лицензию Oracle Java: github.com/google/guava/issues/2960
Flow
91

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

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

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

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

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

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

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

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

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

Берт F
источник
3
Кажется великолепным, и я хотел бы использовать это, но не могу. Почему GPL? Не может ли это быть LGPL вместо этого?
Буркхард
13
В соответствии с часто задаваемыми вопросами : «Более разрешающая лицензия MIT применяется к коду, который вы, возможно, захотите включить в свою собственную программу, например к аннотациям».
Сеан
1
Ссылки в настоящее время не работают. Но +1 за совет по использованию Checker Framework.
Пол Уогланд
1
Жаль, что в последнем выпуске пропущены контролеры неизменности.
Франклин Ю
1
Checker Framework также предлагается в Oracle Java Tutorials .
Quazi Irfan
55

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

Сэм Барнум
источник
68
Обновление: IntelliJ теперь поддерживает все вышеперечисленные аннотации для выделения кода, поэтому вы больше не ограничены аннотациями IntelliJ: blogs.jetbrains.com/idea/2011/03/…
Даниэль Алексюк
31
И Eclipse Juno тоже!
jFrenetic
5
javax.annotation.Nonnullболее широко принят, не так ли?
Мартин
1
@DanielAlexiuc Но, к сожалению, он не использует их для своих проверок во время выполнения, поэтому все еще есть преимущество в использовании JetBrains ...
Trejkaz
4
@Trejkaz С 2016.3 он создает проверки во время выполнения для всех этих.
Кароль С
32

Согласно списку возможностей 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.

Стивен С
источник
4
@pst - текущий график выхода Java 8 в общий выпуск в сентябре 2013 года - infoq.com/news/2012/04/jdk-8-milestone-release-dates
Стивен С.
2
Это произошло до марта 2014 года - openjdk.java.net/projects/jdk8 . JSR 308 включен в сборку M7 (см. «104 - Аннотации на типах Java»).
Стивен С.
28

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

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

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

Джеймс Уолд
источник
3
Было бы полезно представить обоснование этой рекомендации.
абрикос
2
tools.android.com/tech-docs/support-annotations "Сама библиотека поддержки также помечена этими аннотациями, поэтому, как пользователь библиотеки поддержки, Android Studio уже проверит ваш код и отметит потенциальные проблемы на основе этих аннотаций. «.
Джеймс Уолд
3
Кстати, Android Studio поддерживает также jsr305 с javax.annotation.*аннотациями
CAMOBAP
19

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

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations</artifactId>
    <version>15.0</version>
</dependency> 
Бруно Эберхард
источник
Это тот, который заставляет Intellij выдавать предупреждения, да.
Нажмите Upvote
Текущая версия (по состоянию на 05/2017) - 15.0
BamaPookie
Твое право. Я обновил версию. Даже если я предполагаю, что это не сильно изменилось.
Бруно Эберхард
Помните, что аннотации JetBrains не сохраняются для среды выполнения, поэтому, например, поддержка Guice @Nullable не работает с ней.
Питер Майор
18

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

Гили
источник
2
Исправлена ​​проблема с showtopper для maven. Так что это должен быть вариант снова.
Марк фон Рентельн
Я использую FindBugs через Maven, моя IDE ничего не делает, это позволяет избежать конкретных аннотаций IDE, что бы вы порекомендовали?
Кристоф Русси
@ChristopheRoussy Ваш вопрос относится к среде IDE. Пожалуйста, откройте отдельный вопрос.
Гили
15

Различают статический анализ и анализ времени выполнения. Используйте статический анализ для внутренних вещей и анализ времени выполнения для открытых границ вашего кода.

Для вещей, которые не должны быть нулевыми:

  • Проверка во время выполнения: используйте «if (x == null) ...» (нулевая зависимость) или @ javax.validation.NotNull (с проверкой bean-компонента) или @ lombok.NonNull (простой и простой) или guavas Preconditions.checkNotNull (.. .)

    • Используйте Необязательный для типов возвращаемых методов (только). Либо Java8, либо гуава.
  • Статическая проверка: используйте аннотацию @NonNull

  • Там, где это уместно, используйте аннотации @ ... NonnullByDefault на уровне класса или пакета. Создайте эти аннотации самостоятельно (примеры легко найти).
    • Иначе, используйте @ ... 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.
  • аннотации jetbrains, eclipse или checkersframework для статической проверки имеют преимущество перед javax.annotations в том, что они не конфликтуют с другими модулями в Java9 и выше
  • @ javax.annotations.Nullable не означает для Findbugs / Spotbugs то, что вы (или ваша IDE) думаете, что это значит. Findbugs проигнорируют это (на участниках). Грустно, но верно ( https://sourceforge.net/p/findbugs/bugs/1181 )
  • Для статической проверки вне IDE существует 2 бесплатных инструмента: Spotbugs (ранее Findbugs) и checkersframework.
  • Библиотека Eclipse имеет @NonNullByDefault, jsr305 имеет только @ParametersAreNonnullByDefault. Это просто удобные обертки, применяющие базовые аннотации ко всему в пакете (или классе), вы можете легко создавать свои собственные. Это может быть использовано на упаковке. Это может конфликтовать с сгенерированным кодом (например, lombok).
  • Следует избегать использования lombok в качестве экспортированной зависимости для библиотек, которыми вы делитесь с другими людьми: чем меньше транзитивных зависимостей, тем лучше
  • Использование инфраструктуры проверки Bean является мощным, но требует больших накладных расходов, так что это излишне, просто чтобы избежать ручной проверки нуля.
  • Использование Optional для полей и параметров метода является спорным (об этом легко найти статьи)
  • Нулевые аннотации Android являются частью библиотеки поддержки Android, они поставляются с множеством других классов и не очень хорошо работают с другими аннотациями / инструментами.

До Java9 это моя рекомендация:

// 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);
    }
}

Обратите внимание, что нет способа заставить Spotbugs выдавать предупреждение при разыменовании параметра метода, допускающего значение NULL (на момент написания, версия 3.1 Spotbugs). Может быть, проверочная схема может сделать это.

К сожалению, эти аннотации не делают различий между случаями открытого метода библиотеки с произвольными местами вызова и непубличными методами, в которых может быть известен каждый из них. Таким образом, двойное значение: «Укажите, что null нежелателен, но, тем не менее, подготовьтесь к тому, что null будет передан», невозможно в одном объявлении, поэтому в приведенном выше примере есть разные аннотации для интерфейса и реализации.

Для случаев, когда подход с разделенным интерфейсом не практичен, следующий подход является компромиссом:

        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);
        }

Это помогает клиентам не передавать ноль (писать правильный код), возвращая при этом полезные ошибки, если они это делают.

tkruse
источник
Я нашел этот ответ только сейчас, но @tkruse, где вы нашли это: «Аннотации Eclipse jdt не применимы к возвращениям статического метода и некоторым другим случаям»? (первая часть не соответствует действительности, вторая довольно расплывчатая :)).
Стефан Херрманн
@StephanHerrmann: я не могу вспомнить. Я удалил точку пули.
tkruse
12

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

org.eclipse.jdt.annotation.NonNull

Смотрите подробности на http://wiki.eclipse.org/JDT_Core/Null_Analysis .

Horcrux7
источник
Похоже, что это будет интегрировано с Eclipse 3.8 (Juno), что приведет Eclipse в соответствие с IntelliJ в этом отношении. Также он должен позволять вам настраивать ваши собственные аннотации Null (например, javax.annotation.Nonnull) и иметь возможность иметь значение NotNull по умолчанию.
Мотти Стром
11

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

Офир Радниц
источник
7

К сожалению, JSR 308не добавит больше значений, чем этот проект локального предложения Not Null здесь

Java 8не будет поставляться с единственной аннотацией по умолчанию или собственной Checkerструктурой. Подобный Find-Bugs или JSR 305, этот JSR плохо обслуживается небольшой группой в основном академических команд.

Нет коммерческой силой , стоящей за ним, таким образом , JSR 308запуски EDR 3(Early не проект обзора в JCP) сейчас, в то время как Java 8предполагается грузить менее чем за 6 месяцев: -О Подобно 310кстати. но в отличие от того, 308 Oracleчто взял на себя ответственность за это сейчас, от его основателей, чтобы минимизировать вред, который он нанесет платформе Java.

Каждый проект, поставщик и академический класс, подобные тем, которые стоят за, Checker Frameworkи JSR 308создадут собственную проприетарную аннотацию проверки.

Делать исходный код несовместимым на долгие годы, пока не будут найдены несколько популярных компромиссов и, возможно, добавлены в Java 9или 10, или через фреймворки, такие как Apache Commonsили Google Guava;-)

Вернер Кейл
источник
7

Android

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

Чтобы добавить пакет support-annotations , добавьте следующую зависимость в ваш build.gradle:

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

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

import android.support.annotation.NonNull;

void foobar(@NonNull Foo bar) {}
Шубхам Чаудхари
источник
5

В ожидании того, что это будет рассмотрено вверх по течению (Java 8?), Вы также можете просто определить свой собственный локальный проект @NotNullи @Nullableаннотации. Это может быть полезно также в случае, если вы работаете с Java SE, где по умолчанию 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 {}

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

Арто Бендикен
источник
4

Если вы разрабатываете для Android, вы несколько связаны с Eclipse (редактировать: на момент написания, больше нет), который имеет свои собственные аннотации. Он включен в Eclipse 3.8+ (Juno), но по умолчанию отключен.

Вы можете включить его в «Предпочтения»> «Java»> «Компилятор»> «Ошибки / предупреждения»> «Нулевой анализ» (сворачиваемый раздел внизу).

Установите флажок «Включить нулевой анализ на основе аннотаций»

http://wiki.eclipse.org/JDT_Core/Null_Analysis#Usage содержит рекомендации по настройке. Однако, если у вас есть внешние проекты в вашей рабочей области (например, SDK facebook), они могут не соответствовать этим рекомендациям, и вы, вероятно, не хотите исправлять их при каждом обновлении SDK ;-)

Я использую:

  1. Доступ к нулевому указателю: ошибка
  2. Нарушение нулевой спецификации: ошибка (связана с пунктом 1)
  3. Потенциальный доступ к нулевому указателю: Предупреждение (в противном случае SDK facebook будет иметь предупреждения)
  4. Конфликт между нулевыми аннотациями и нулевым выводом: Предупреждение (связано с пунктом № 3)
chaqke
источник
4
привязан к затмению? Не правда.
dcow
1
@DavidCowden IntelliJ IDEA с поддержкой разработки Android, я думаю, был доступен за некоторое время до того, как AndroidStudio был взломан.
Мартиньш Бридис,
@ MārtiņšBriedis да, это правда. Я думаю, что вы имели в виду @chaqke.
13
Стоит отметить, что android и intellij имеют отдельные аннотации и, вероятно, останутся такими до тех пор, пока java не включит официальные аннотации. это инструкции по использованию аннотаций eclipse с eclipse.
Chaqke
Он никогда не был привязан к Затмению. Вы можете использовать любую IDE, какую захотите.
DennisK
4

Если вы работаете над большим проектом, возможно, вам лучше создавать свои собственные @Nullable и / или @NotNullаннотации.

Например:

@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?

Нажмите «Полицейский» в правом нижнем углу строки состояния IntelliJ. И нажмите «Настроить проверки» во всплывающем окне. Следующий ... настроить аннотации

bvdb
источник
1
Я попробовал ваш совет, но ideaничего не говорят о void test(@NonNull String s) {}называютtest(null);
user1244932
3
@ user1244932 Вы имеете в виду IntelliJ IDEA? Вы можете настроить аннотации обнуляемости, которые он использует для статического анализа. Я точно не знаю, где, но одно место для их определения - «Файл> Настройки> Сборка, Выполнение, Развертывание> Компилятор», и там есть кнопка «Настроить аннотации ...».
Adowrath
@ user1244932 смотрите скриншот, если вы все еще ищете это.
bvdb
3

Здесь уже слишком много ответов, но (а) сейчас 2019 год, и до сих пор нет «стандарта» Nullableи (б) нет других ответов, ссылающихся на Котлина.

Ссылка на Kotlin важна, потому что Kotlin на 100% совместим с Java и имеет базовую функцию нулевой безопасности. При вызове библиотек Java можно воспользоваться этими аннотациями, чтобы сообщить инструментам Kotlin, может ли API-интерфейс Java принимать или возвращать null.

Насколько я знаю, единственными Nullableпакетами, совместимыми с Kotlin, являются org.jetbrains.annotationsи android.support.annotation(сейчас androidx.annotation). Последний совместим только с Android, поэтому его нельзя использовать в проектах JVM / Java / Kotlin, отличных от Android. Тем не менее, пакет JetBrains работает везде.

Поэтому, если вы разрабатываете пакеты Java, которые также должны работать в Android и Kotlin (и поддерживаться Android Studio и IntelliJ), ваш лучший выбор, вероятно, - пакет JetBrains.

Maven:

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations-java5</artifactId>
    <version>15.0</version>
</dependency>

Gradle:

implementation 'org.jetbrains:annotations-java5:15.0'
noamtm
источник
2
Хм, это говорит иначе: kotlinlang.org/docs/reference/…
skagedal
3

Есть еще один способ сделать это в Java 8. Я делаю 2 вещи, чтобы выполнить то, что мне нужно:

  1. Делая обнуляемые поля явными с типами, оборачивая обнуляемые поля java.util.Optional
  2. Проверка того, что все ненулевые поля не равны нулю во время построения с java.util.Objects.requireNonNull

Пример:

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);
  }

Так что мой вопрос, нужно ли нам даже комментировать при использовании Java 8?

Изменить: Позже я узнал, что некоторые считают плохую практику для использования Optionalв аргументах, здесь есть хорошая дискуссия с плюсами и минусами Почему Java 8 необязательно не использовать в аргументах

Альтернативный вариант, учитывая, что не рекомендуется использовать Optional в аргументах, нам нужно 2 конструктора:

  //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();
      }
Моцарт Броккини
источник
Я бы сказал, что вам все еще нужна аннотация @NotNull для всех 4-х формальных параметров, чтобы контролеры статического анализа знали ваше намерение, чтобы ни один из них не был нулевым. В языке Java пока нет ничего, что могло бы обеспечить это. Вы также должны проверить, что описание не является нулевым, если вы программируете в обороне.
Джаксин
2
Я все еще могу написать этот код new Role(null,null,null,null);. С аннотациями мой IDE и статический анализ будут предупреждать, что в эти параметры нельзя передать нуль. Без этого я не узнаю, пока не выполню код. Это ценность аннотаций.
Джаксин
2
Я также нахожусь в среде, где разработчики могут использовать любую IDE или текстовый редактор, который они предпочитают, он не является взаимоисключающим. Затем мы также интегрируем maven-pmd-plugin и / или SonarQube в процесс компоновки, чтобы поощрять и выделять и даже устранять проблемы качества кода перед объединением, например, при запросах на извлечение.
Джаксин
2
Необязательный не предназначен для использования в качестве аргумента метода или частного поля. См. Например: stuartmarks.wordpress.com/2016/09/27/vjug24-session-on-optional
assylias
1
@assylias да, я узнал это позже, они говорят, что это не рекомендуется, потому что это нам ничего не купит, я определенно могу понять их рациональность. В этом случае, я поместил здесь, можно сделать аргумент description не нулевым, и клиентский код мог бы передать пустую строку, но во многих случаях было бы удобно различать пустую строку и не иметь значения. Спасибо за ваш комментарий. Я обновлю ответ.
Моцарт Броккини
2

Разве у Солнца нет своего? Что это:
http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Modules-com.sun/istack/com.sun.istack.internal.htm

Это, кажется, упаковано со всеми версиями Java, которые я использовал в течение прошлых нескольких лет.

Изменить: как уже упоминалось в комментариях ниже, вы, вероятно, не хотите использовать их. В этом случае мой голос за аннотации IntelliJ Jetbrains!

Нейт У.
источник
10
Я понятия не имею, что это такое, но имя пакета должно быть БОЛЬШОЙ КЛЮЧОМ, которое НЕ предназначено для общего использования.
Стивен С
3
Обычно воздерживаются от использования классов в пространстве имен com.sun, поскольку они являются внутренними; не предназначен для непосредственного использования; и без каких-либо гарантий относительно их доступности или поведения в будущем. Нужно иметь действительно солидный случай, чтобы напрямую использовать артефакт com.sun.
luis.espinal
плюс что-то, отображаемое в таком плохом HTML-формате (на Java2s.com, чтобы завершить его) должно дать вам красные флажки :)
luis.espinal
2

Одно из преимуществ IntelliJ заключается в том, что вам не нужно использовать их аннотации. Вы можете написать свой собственный или использовать любой другой инструмент, который вам нравится. Вы даже не ограничены одним типом. Если вы используете две библиотеки, которые используют разные аннотации @NotNull, вы можете указать IntelliJ использовать обе из них. Для этого перейдите в «Настроить проверки», нажмите «Постоянные условия и исключения» и нажмите кнопку «Настроить проверки». Я использую Nullness Checker везде, где могу, поэтому я настроил IntelliJ для использования этих аннотаций, но вы можете заставить его работать с любым другим инструментом, который вам нужен. (У меня нет мнения о других инструментах, потому что я использую проверки IntelliJ в течение многих лет, и я люблю их.)

MiguelMunoz
источник
1

Другой вариант заключается в аннотации , снабженный ANTLR 4. После Прицепных Request # 434 , артефакт , содержащий @NotNullи @Nullableаннотацию включает в себя процессор , который производит аннотации ошибок и / или предупреждения во время компиляции в одном событии из этих атрибутов неправильно (например, если оба применяются к одному и тому же элементу, или если @Nullableприменяются к элементу с примитивным типом). Процессор аннотаций обеспечивает дополнительную гарантию в процессе разработки программного обеспечения, что информация, передаваемая с помощью этих аннотаций, является точной, в том числе в случаях наследования методов.

Сэм Харвелл
источник
1

Если вы создаете свое приложение с использованием Spring Framework, я бы предложил использовать javax.validation.constraints.NotNullComing from Beans Validation, упакованный в следующую зависимость:

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>

Основным преимуществом этой аннотации является то, что Spring обеспечивает поддержку как параметров метода, так и полей класса, помеченных как javax.validation.constraints.NotNull. Все, что вам нужно сделать, чтобы включить поддержку:

  1. поставьте jar api для проверки bean-компонентов и jar с реализацией валидатора аннотаций jsr-303 / jsr-349 (который поставляется с зависимостью Hibernate Validator 5.x):

    <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>
  2. предоставить MethodValidationPostProcessor для контекста Spring

      @Configuration
      @ValidationConfig
      public class ValidationConfig implements MyService {
    
            @Bean
            public MethodValidationPostProcessor providePostProcessor() {
                  return new MethodValidationPostProcessor()
            }
      }
  3. наконец, вы аннотируете ваши классы Spring, org.springframework.validation.annotation.Validatedи Spring будет автоматически обрабатывать валидацию.

Пример:

@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 в качестве значения параметра, пружина (с помощью HibernateValidator) сработает ConstraintViolationException. Нет необходимости в ручной работе здесь.

Вы также можете проверить возвращаемые значения.

Еще одно важное преимущество использования javax.validation.constraints.NotNullBeans Validation Framework заключается в том, что на данный момент он все еще разрабатывается и для новой версии 2.0 запланированы новые функции.

Как насчет @Nullable? Ничего подобного в Beans Validation 1.1 нет. Что ж, я могу поспорить, что если вы решите использовать, то @NotNullвсе, что НЕ аннотировано, @NonNullэффективно «обнуляется», поэтому @Nullableаннотация бесполезна.

walkeros
источник
1
Пожалуйста, не используйте его. Он используется для проверки во время выполнения, а не для статического анализа кода. См. Justsomejavaguy.blogspot.com/2011/08/… для получения подробной информации. Источник: УДАЛЕН ответ с 219 голосами от @ luis.espinal.
Коппор
@koppor: я не согласен. Если это не предназначено для использования, зачем Spring обрабатывает это во время выполнения. Кроме того, структура проверки Beans позволяет создавать аннотации исключительно для анализа во время выполнения, поскольку она позволяет получить доступ к объекту Context (в настоящее время аннотированный / проверенный экземпляр) во время выполнения.
Walkeros
0

Spring 5 имеет @NonNullApi на уровне пакета. Это кажется удобным выбором для проекта, который уже имеет зависимости Spring. Все поля, параметры и возвращаемые значения по умолчанию @NonNull и @Nullable могут быть применены в нескольких местах, которые отличаются.

Файл package-info.java:

@org.springframework.lang.NonNullApi
package com.acme;

https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories.nullability.annotations

Кен Джаррад
источник