Яку анотацію на @NotNull Java слід використовувати?


997

Я хочу зробити свій код більш читабельним, а також використовувати інструменти, такі як перевірка коду IDE та / або статичний аналіз коду (FindBugs та Sonar), щоб уникнути NullPointerExceptions. Багато інструментів здаються несумісними з анотацією @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, надається документацією на пакет підтримки

  • org.eclipse.jdt.annotation.NonNull
    Використовується Eclipse для документації з аналізу статичного коду


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

6
@irreputable, якщо apache винайде нове "загальне", було б 56 його версій, що збігаються з іншими проектами. І це все одно не буде стандартним (стандарт! = Широко розповсюджений). Краще використовувати що-небудь справді стандартне, javax? *. До речі, у цих прикладах немає "занадто багато стандартів", я просто бачу 1 або 2.
ymajoros

6
javax.annotation.Nonnull працює з Findbugs (тільки що його протестували), що є вагомою причиною для його використання.
Nicolas C

20
Якщо я просто пишу @NotNull, це стосується com.sun.istack.internal.NotNull. OMG ...
Томас Веллер

3
@MozartBrocchini - необов'язкові параметри корисні у випадках, коли ви, можливо, раніше використовували NullObjects. Вони насправді не вирішують ту саму мету, що й анотація під час виконання \ @NotNull, і вони вводять нудне розгортання.
Дейв

Відповіді:


205

Оскільки 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)).

Як описано в цій відповіді на відповідь, вплив анотацій під час виконання є меншим, ніж можна було б подумати, але вони мають перевагу в тому, що вони дозволяють інструментам робити перевірки виконання на додаток до даних про час компіляції.

Ще одна важлива відмінність полягає в тому, де в коді можна використовувати анотації. Є два різні підходи. Деякі пакети використовують контексти стилів JLS 9.6.4.1. Наступна таблиця дає огляд:

                                ПАРАМЕТР ПОЛЬНОГО МЕТОДУ LOCAL_VARIABLE 
android.support.annotation XXX   
edu.umd.cs.findbugs.annotations XXXX
org.jetbrains.annotation XXXX
ломбок 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).
robinst

10
@kevinarpe: Проект Findbugs мертвий, а наступний проект Spotbugs видаляє ці примітки: github.com/spotbugs/spotbugs/pull/180
robinst

4
JSR 305 , який був би стандартизований javax.annotation.NonNull, так і не завершився, оскільки його технічні характеристики дісталися AWOL. Це не мало нічого спільного з жодним рішенням Oracle.
Марк Рейнхолд

5
Ще одна причина не використовувати jsr305.jar полягає в тому, що він, очевидно, порушує бінарну ліцензію Java Oracle: github.com/google/guava/isissue/2960
Потік

91

Мені дуже подобається Checker Framework , який є реалізацією анотацій типів ( JSR-308 ), яка використовується для впровадження перевірок дефектів на зразок перевірки нульовості. Я ніколи не намагався будь-який інший запропонувати порівняння, але був задоволений цією реалізацією.

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

У цій системі мені подобаються чотири речі:

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

  2. Налаштування за замовчуванням для перевірки недійсності працює добре: Non-null, за винятком локальних (NNEL). В основному це означає, що за замовчуванням перевіряюча програма обробляє кожне (змінні екземпляра, параметри методу, загальні типи тощо), за винятком локальних змінних, як якщо б вони за замовчуванням мають тип @NonNull. Відповідно до документації:

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

    Ви можете встановити інший за замовчуванням для класу або методу, якщо NNEL не працює для вас.

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

  4. Рамка має можливість анотувати API, який ви використовуєте, який ще не позначений для недійсності за допомогою файлів заглушки.


3
Здається чудово, і я хотів би ним скористатися, але не можу. Чому GPL? Чи не міг це бути LGPL?
Буркхард

13
Відповідно до поширених запитань : "Більш дозвільна ліцензія MIT застосовується до коду, який ви хочете включити у власну програму, наприклад, примітки".
seanf

1
Зараз посилання розірвані. Але +1 за порадою щодо використання Checker Framework.
Пол Вагленд

1
Шкода, що шашки незмінність скинули в останньому випуску.
Франклін Ю

1
Checker Framework також пропонується в навчальних посібниках Java Oracle .
Куазі Ірфан

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 він створює перевірки часу виконання для всіх цих.
Кароль S

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
Stephen C

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
BTW 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 є автором тієї самої людини. Обидва погано підтримуються, але настільки ж стандартні, як це отримується, і підтримуються всіма основними ІДЕ. Хороша новина в тому, що вони добре працюють.

Ось як застосувати @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 308 є перетворення його в JDK 8 , і хоча JSR не визначає @NotNull, супроводжуючий Checkers Frameworkробить. На момент написання цього запису плагін Maven непридатний через цю помилку: https://github.com/typetools/checker-framework/isissue/183


2
Випуск showstopper для Maven виправлений. Тож це знову має бути варіант.
Марк фон Рентелн

Я використовую FindBugs через Maven, нічого не робиться моїм IDE, це уникає специфічних анотацій IDE, що б ви порадили?
Крістоф Руссі

@ChristopheRoussy Ваше запитання стосується IDE. Будь ласка, відкрийте окреме запитання.
Гілі

15

Розрізняють статичний аналіз та аналіз часу виконання. Використовуйте статичний аналіз для внутрішніх речей та аналізу виконання для загальнодоступних меж вашого коду.

Для речей, які не повинні бути нульовими:

  • Перевірка виконання: Використовуйте "if (x == null) ..." (нульова залежність) або @ javax.validation.NotNull (з валідацією квасолі) або @ 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
  • Анотації спотбугів все ще залежать від приміток jsr305 / findbugs під час компіляції (під час написання https://github.com/spotbugs/spotbugs/isissue/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 існує два безкоштовні інструменти: Spotbugs (раніше Findbugs) і шашки.
  • Бібліотека Eclipse має @NonNullByDefault, jsr305 має лише @ParametersAreNonnullByDefault. Це просто обгортки зручності, застосовуючи базові анотації до всього пакету (або класу), ви можете легко створити свій власний. Це можна використовувати на упаковці. Це може суперечити створеному коду (наприклад, lombok).
  • Використання lombok як експортованої залежності слід уникати для бібліотек, якими ви ділитесь з іншими людьми, чим менше перехідних залежностей, тим краще
  • Використання структури перевірки Bean є потужним, але вимагає великих витрат, тому це зайве, щоб уникнути ручної перевірки нуля.
  • Використання необов'язкових для полів та параметрів методів є спірним (ви можете легко знайти статті про нього)
  • Нульові анотації на 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 підняти попередження, коли параметр методу, що зводиться до нуля, буде скасовано (на момент написання, версія 3.1 Spotbugs). Можливо, контрольна рамка може це зробити.

На жаль, ці анотації не розрізняють випадків публічного методу бібліотеки з довільними позивними позиціями та непублічними методами, коли кожний виклик може бути відомий. Отже, подвійне значення: "Вкажіть, що нуль небажаний, але все ж готуйтеся до передачі нуля" неможливо в одній декларації, отже, вищевказаний приклад має різні примітки для інтерфейсу та реалізації.

У випадках, коли підхід розділеного інтерфейсу не є практичним, наступний підхід є компромісом:

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

Це допомагає клієнтам не пропускати null (написання правильного коду), при цьому повертаючи корисні помилки, якщо вони є.


Цю відповідь я знайшов лише зараз, але @tkruse, де ти це знайшов: "Анотації jclipse jdt не застосовуються до повернень статичного методу та деяких інших випадків"? (перша частина не вірна, друга досить розпливчаста :)).
Стефан Геррман

@StephanHerrmann: Я не можу згадати. Я зняв кульову точку.
tkruse

12

Затьмарення також має свої примітки.

org.eclipse.jdt.annotation.NonNull

Докладні відомості див. На веб- сайті http://wiki.eclipse.org/JDT_Core/Null_Analysis .


Схоже, це буде інтегровано з 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(Ранній проект огляду в JCP) ЗАРАЗ, тоді як, як Java 8передбачається, поставляється менше ніж за 6 місяців: -O Схоже на 310btw. але на відміну від308 Oracle цього взяв на себе відповідальність від своїх засновників, щоб мінімізувати шкоду, який він завдасть платформі Java.

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

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


7

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

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)

4
прив’язаний до затемнення? Неправда.
цві

1
@DavidCowden IntelliJ IDEA з підтримкою Android-розробки, я думаю, був доступний за деякий час до введення AndroidStudio.
Mārtiņš Briedis

@ MārtiņšBriedis так, це правда. Я думаю, ти мав на увазі @chaqke.
dcow

Варто зазначити, що для android та intellij є окремі анотації, і вони, ймовірно, залишаться таким чином, поки java не включить офіційні анотації. це вказівки щодо використання приміток eclipse із затемненням.
чакке

Він ніколи не був прив’язаний до затемнення. Ви можете використовувати будь-який 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. І натисніть "Налаштувати перевірки" у спливаючому вікні. Далі ... налаштування приміток


1
Я спробував вашу пораду, але ideaнічого не кажіть про void test(@NonNull String s) {}дзвонивtest(null);
user1244932

3
@ user1244932 Ви маєте на увазі IntelliJ IDEA? Ви можете налаштувати анотації про зменшення, які він використовує для статичного аналізу. Я точно не знаю куди, але одне місце для їх визначення - у "Файл> Налаштування> Збірка, Виконання, Розгортання> Компілятор", а там знаходиться кнопка "Налаштувати анотації ...".
Adowrath

@ user1244932 дивіться скріншот, якщо ви все ще шукаєте цього.
bvdb

3

Тут вже занадто багато відповідей, але (а) це 2019 рік, і досі немає «стандартних» Nullableта (б) жодних інших посилань на відповіді Котліна.

Посилання на Kotlin є важливим, оскільки Kotlin на 100% сумісний з Java, і він має основну функцію Null Safety. Викликаючи бібліотеки Java, можна скористатися цими примітками, щоб повідомити інструменти Kotlin, чи може API API приймати або повертати 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'

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 в аргументах

Альтернативний варіант, враховуючи те, що не рекомендується використовувати необов'язково в аргументах, нам потрібні 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 це не примушує. Ви також повинні перевірити, що опис є недійсним, якщо ви програмуєте захисно.
jaxzin

2
Я все ще можу написати цей код new Role(null,null,null,null);. З анотаціями мій IDE та статичний аналіз попередить, що null не може бути переданий у ці параметри. Без нього я не дізнаюся, поки не запущу код. Ось цінність приміток.
jaxzin

2
Я також перебуваю в середовищі, де розробники можуть використовувати будь-який IDE або текстовий редактор, який вони віддають перевагу, але це не є взаємовиключним. Потім ми також інтегруємо плагін maven-pmd та / або SonarQube в процес збирання, щоб заохотити і виділити, і навіть ворота, питання якості коду попередньо об'єднати, наприклад, на запити на виклик.
jaxzin

2
Необов’язково не передбачається використовувати як аргумент методу або приватне поле. Дивіться наприклад: stuartmarks.wordpress.com/2016/09/27/vjug24-session-on-optional
assylias

1
@assylias так, я виявив, що пізніше, вони кажуть, що це не рекомендується, оскільки він нічого не купить нам, я можу точно зрозуміти їх раціональність. У цьому випадку я ставлю сюди, можна зробити аргумент description недійсним, і клієнтський код міг передати порожню рядок, але в багатьох випадках це було б зручно розрізняти і порожній String і не мати значення. Дякуємо за ваш коментар Я оновлю відповідь.
Моцарт Броккіні

2

Зараз у сонця немає власних? Що це:
http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Modules-com.sun/istack/com.sun.istack.internal.htm

Здається, це в комплекті з усіма версіями Java, якими я користувався протягом останніх кількох років.

Редагувати: Як було сказано в коментарях нижче, ви, ймовірно, не хочете їх використовувати. У такому випадку моє голосування - за анотації на мотоцикли IntelliJ!


10
Я поняття не маю, що це таке, але назва пакету повинна бути ВЕЛИКОЮ КЛАСУ, що НЕ призначена для загального використання.
Stephen C

3
Зазвичай людина не вдається використовувати класи в просторі імен com.sun, оскільки вони є внутрішніми; не призначений для прямого використання; і без жодних гарантій щодо їх майбутньої доступності чи поведінки. Треба мати справді надійний випадок, щоб безпосередньо використовувати артефакт com.sun.
luis.espinal

плюс щось, що відображається у такому поганому форматі HTML (на Java2s.com, щоб доповнити його), має дати вам червоні прапори :)
luis.espinal

2

Однією з приємних речей щодо IntelliJ є те, що вам не потрібно використовувати їх анотації. Ви можете написати свій власний або будь-який інший інструмент, який вам подобається. Ви навіть не обмежені одним типом. Якщо ви використовуєте дві бібліотеки, які використовують різні анотації @NotNull, ви можете сказати IntelliJ використовувати обидві. Для цього перейдіть до "Налаштувати перевірки", натисніть на інспекцію "Постійні умови та винятки" та натисніть кнопку "Налаштувати перевірки". Я використовую інструмент перевірки нульовості, де тільки можу, тому я налаштував IntelliJ на використання цих приміток, але ви можете змусити його працювати з будь-яким іншим інструментом, який ви хочете. (Я не маю думки щодо інших інструментів, тому що я вже багато років використовую перевірки IntelliJ, і я їх люблю.)


1

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


1

Якщо ви будуєте свою програму за допомогою 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 для перевірки квасолі та банку з реалізацією валідатора приміток jsr-303 / jsr-349 (який поставляється із залежністю до сплячого валідатора 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 контексту весни

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

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

Ще одна важлива перевага, що javax.validation.constraints.NotNullвипливає для валідації Beans Framework, полягає в тому, що на даний момент вона все ще розробляється і плануються нові функції для нової версії 2.0.

Про що @Nullable? У валідації бобів 1.1 немає нічого подібного. Ну, я можу стверджувати, що якщо ви вирішите використовувати, @NotNullніж все, на що НЕ зазначається, це фактично "зведене нанівець @NonNull", тому @Nullableанотація марна.


1
Будь ласка, не використовуйте його. Він використовується для перевірки часу виконання, а не для статичного аналізу коду. Докладні відомості див. У justsomejavaguy.blogspot.com/2011/08/… . Джерело: ВИДАЛЕНА відповідь з 219 голосами від @ luis.espinal.
коппор

@koppor: Я не згоден. Якщо це не призначено для використання, чому Spring би обробляв це під час виконання. Також рамка перевірки Beans дозволяє створювати анотації виключно для аналізу часу виконання, оскільки дозволяє отримати доступ до об'єкта Context (на даний момент анотованого / перевіреного instancje) під час виконання.
walkeros

0

Весна 5 має @NonNullApi на рівні пакету. Це здається зручним вибором для проекту, який вже має весняні залежності. Усі поля, параметри та значення повернення за замовчуванням до @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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.