У чому сенс java.util. @ Nullable?


56

Я читаю код Гуави, де я знайшов примітку java.util.@Nullableв якомусь коді. Я знаю значення @Nullable, але цього не розумію. Зокрема, я не можу знайти клас, який називається Nullableв пакеті java.util. Будь-ласка, скажіть мені, який сенс цього java.util.@Nullable:

public static <T> java.util.@Nullable Optional<T> toJavaUtil(
    @Nullable Optional<T> googleOptional) {
  return googleOptional == null ? null : googleOptional.toJavaUtil();
}

4
@PaulRooney Я так не думаю. NullableВони говорять про те , як представляється, в javax.annotationНЕ java.util.
Джозеф Сибл-

3
Цікаво, чи це може бути просто дивний синтаксис-Java. Зокрема, чи може це в основному просто означати public static <T> @Nullable java.util.Optional<T> toJavaUtil, але що Java просто не дозволяє вам так написати?
Джозеф Зібл-

1
@JosephSible public static <T> @Nullable java.util.Optional<T> toJavaUtilнасправді неможливий, але немає жодної причини використовувати FQN, Optionalколи імпорт вже існує.
Том

6
@Tom Є ще одна Optionalсфера застосування.
Джозеф Зібл-

2
дякую хлопці, я знайшов це в Checker Framework. @Nullable java.util.List The correct Java syntax to write an annotation on a fully-qualified type name is to put the annotation on the simple name part, as in java.util.@Nullable List. But, it’s usually better to add import java.util.List to your source file, so that you can just write @Nullable List.
SetDaemon

Відповіді:


37

Рядок public static <T> java.util.@Nullable Optional<T> toJavaUtilнаписаний так, оскільки звичайний стиль public static <T> @Nullable java.util.Optional<T> toJavaUtilнедійсний. Це визначено у § 9.7.4 JLS :

Це помилка часу компіляції, якщо анотація типу T застосовується до типу (або будь-якої частини типу) у контексті типу, а T застосовна у контекстах типу, а примітка неприпустима.

Наприклад, припустімо тип анотації TA, який мета-анотований з просто @Target(ElementType.TYPE_USE). Терміни @TA java.lang.Objectі java.@TA lang.Objectє незаконними , так як просте ім'я , до якого @TA ближче класифікуються як ім'я пакета. З іншого боку, java.lang.@TA Objectце законно.

Декларація типу org.checkerframework.checker.nullness.qual@Nullable:

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

Тож воно застосовується до цього правила.

Що ця структура не порушує виконання, оскільки java.utilім'я пакета та класу Optionalбуло розділене, можна побачити, коли ми подивимось на складений код, використовуючи javap -c [compiled class name]:

class just.a.test.Main {
  just.a.test.Main();
    Code:
       0: aload_0
       1: invokespecial #1          // Method java/lang/Object."<init>":()V
       4: return

  public static <T> java.util.Optional<T> toJavaUtil(blub.Optional<T>);
    Code:
       0: aload_0
       1: ifnonnull     8
       4: aconst_null
       5: goto          12
       8: aload_0
       9: invokevirtual #2          // Method blub/Optional.toJavaUtil:()Ljava/util/Optional;
      12: areturn
}

( blub.Optionalце місцевий клас, куди я скопіював код Guava, щоб отримати мінімальний приклад для де- / компілювання)

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


Ця помилка компілятора також застосовується до змінних, таких як:

private @Nullable2 java.util.Optional<?> o;

Але може стати прийнятним, коли анотація додатково отримає цільовий тип ElementType.FIELD, як це написано в тому самому пункті JLS:

Якщо TA додатково мета-анотований @Target(ElementType.FIELD), термін @TA java.lang.Objectє легальним у місцях, які є як деклараційними, так і типовими контекстами, такими як декларація поля @TA java.lang.Object f;. Тут @TA вважається застосованим до декларації f (а не до типу java.lang.Object), оскільки ТА застосовується в контексті декларації на місцях.


11

Під час використання анотацій це синтаксис, який використовується, коли ви хочете написати повністю кваліфіковане ім’я для типу, а не додавати оператор імпорту.

Цитування з рамкового посібника з перевірки :

Правильний синтаксис Java для написання анотації на повністю кваліфіковане ім’я типу - це поміщення примітки до частини простого імені, як у java.util. @ Nullable List. Але, як правило, краще додати імпорт java.util.List до вихідного файлу, щоб ви могли просто написати @Nullable List.

Також згадується на сторінці 2 специфікації JSR308, яку можна завантажити тут . Він говорить:

Анотація типу з’являється перед простим іменем типу, як у @NonNull String або java.lang. @ NonNull String.


6

Дивним бітом тут насправді є незнайомий синтаксис для застосування ElementType.TYPE_USEанотації, що звертається до нього . Якщо ви перевірте документи Nullable , ви побачите незнайомі цілі:

...
@Target(value={TYPE_USE,TYPE_PARAMETER})
<public @interface Nullable
...

Ця анотація використовується безпосередньо перед простим ім'ям коментуваного типу, як у обох наступних випадках:

public static <T> @Nullable Optional<T> toJavaUtil
public static <T> java.util.@Nullable Optional<T> toJavaUtil

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface InterceptedReturnValue {
    boolean value() default true;
}

І обробляли його, використовуючи:

public java.lang.@InterceptedReturnValue(true) String testMethod(String param) {
    return null;
}

public @InterceptedReturnValue(false) String testMethod(int param) {
    return null;
}

public static void main(String[] args) throws Exception {
    Method m = Main.class.getDeclaredMethod("testMethod", String.class);
    if(m.getAnnotatedReturnType().isAnnotationPresent(InterceptedReturnValue.class)) {
        InterceptedReturnValue config = m.getAnnotatedReturnType()
                .getAnnotation(InterceptedReturnValue.class);

        if(config.value()) {
            //logging return value enabled
        }
    }
}

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

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