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

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

Ответы [ 21 ]

7 голосов
/ 01 апреля 2013

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

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

Никакой коммерческой власти за этим не стоит, поэтому JSR 308 запускает EDR 3 (Ранняя проверка проекта в JCP) СЕЙЧАС, тогда как Java 8 предполагается отправить менее чем за 6 месяцев: -O Аналогично 310 между прочимно в отличие от 308 Oracle теперь взял на себя ответственность за это от своих основателей, чтобы минимизировать вред, который он нанесет платформе Java.

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

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

6 голосов
/ 20 января 2017

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

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

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

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

  • Там, где он подходит, используйте аннотации @ ... NonnullByDefault на уровне класса или пакета. Создайте эти аннотации самостоятельно (примеры легко найти).
    • Иначе, используйте @ ... CheckForNull при возврате метода, чтобы избежать NPE

Это должно дать наилучший результат: предупреждения в IDE, ошибки Findbugs и checkerfrawork, значимые исключения во время выполнения.

Не ожидайте, что статические проверки будут зрелыми, их именование не стандартизировано, и разные библиотеки и 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 или checkersfrawork для статической проверки имеют преимущество перед 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).
  • Аннотации Eclipse jdt не применимы к возвращениям статического метода и некоторым другим случаям
  • Следует избегать использования lombok в качестве экспортированной зависимости для библиотек, которыми вы делитесь с другими людьми: чем меньше транзитивных зависимостей, тем лучше
  • Использование инфраструктуры проверки Bean является мощным, но требует больших накладных расходов, так что это излишне, просто чтобы избежать ручной проверки нуля.
  • Использование Optional для полей и параметров метода является спорным (статьи об этом легко найти)
  • Пустые аннотации Android являются частью библиотеки поддержки Android, они поставляются с множеством других классов и не очень хорошо работают с другими аннотациями / инструментами

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

// file: package-info.java
@javax.annotation.ParametersAreNonnullByDefault
package example;


// file: PublicApi
package example;

public class PublicApi {

    /**
     * @param firstname MUST NOT be null
     * @param lastname MUST NOT be null
     */
    public Person createPerson(
            // Spotbugs ignores the param annotations, but IDEs will show problems
            @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). Может быть, это можно сделать с помощью контрольной рамки.

5 голосов
/ 26 июля 2011

В ожидании того, что это будет рассмотрено в восходящем направлении (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 голосов
/ 01 мая 2013

Если вы разрабатываете для 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)
4 голосов
/ 06 февраля 2017

Если вы работаете над большим проектом, вам может быть лучше создать свои собственные @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. И нажмите «Настроить проверки» во всплывающем окне. Следующий ... configure annotations

3 голосов
/ 04 мая 2016

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

  1. Явление полей типа NULL в явном виде с помощью переноса полей Nullable в java.util.Optional
  2. Проверка того, что все ненулевые поля не равны NULL во время построения с помощью 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 в качестве аргумента не следует использовать в аргументах

2 голосов
/ 31 мая 2017

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

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

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

  1. предоставить api jar для проверки bean и jar с реализацией валидатора аннотаций jsr-303 / jsr-349 (который поставляется с Hibernate Validator5.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 10 **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 в качествеЗначение параметра spring (с помощью HibernateValidator) будет выбрасывать ConstraintViolationException.Здесь не требуется ручной работы.

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

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

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

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

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

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

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

2 голосов
/ 28 мая 2019

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

Ссылка на 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'
1 голос
/ 22 января 2014

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

...