В чем смысл java.util. @Nullable?

Я читаю код Guava, где я нашел аннотацию [email protected] в каком-то коде. Я знаю значение @Nullable, но я не понимаю этого. В частности, я не могу найти класс с именем Nullable в пакете java.util. Пожалуйста, кто-нибудь, скажите мне, что означает это [email protected]:

public static <T> [email protected] Optional<T> toJavaUtil(
    @Nullable Optional<T> googleOptional) {
  return googleOptional == null ? null : googleOptional.toJavaUtil();
}

Ответ 1

Строка public static <T> [email protected] Optional<T> toJavaUtil написана так, потому что обычный стиль public static <T> @Nullable java.util.Optional<T> toJavaUtil недопустим. Это определено в JLS §9.7.4:

Это ошибка времени компиляции, если аннотация типа T применяется к типу (или любой части типа) в контексте типа, а T применима в контекстах типа, а аннотация недопустима.

Например, предположим, что тип аннотации TA мета-аннотируется просто @Target(ElementType.TYPE_USE). Термины @TA java.lang.Object и [email protected] lang.Object являются недопустимыми, потому что простое имя, к которому ближе всего @TA, классифицируется как имя пакета. С другой стороны, [email protected] Object является законным.

Объявление типа [email protected]:

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

([TG412] is a local class where I copied the Guava code in, in order to get a minimal example to de-/compile)

Как видите, аннотации там больше не существует. Это только маркер для компилятора, который предотвращает предупреждение, когда метод возвращает 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), потому что TA применяется в контексте объявления поля.

Ответ 2

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

Цитата из руководства по фреймворку:

Правильный синтаксис Java для написания аннотации на полностью квалифицированном имя типа, чтобы поместить аннотацию к простой части имени, как в java.util. @Nullable List. Но обычно лучше добавить импорт java.util.List в ваш исходный файл, так что вы можете просто написать @Обнуляемый список.

Он также упоминается на странице 2 спецификации JSR308, которую можно скачать здесь. Это говорит:

Аннотация типа появляется перед простым именем типа, как в @NonNull String или java.lang. @NonNull String.

Ответ 3

Странным моментом здесь действительно является незнакомый синтаксис для применения аннотации ElementType.TYPE_USE -targeted. Если вы посмотрите Nullable документы, вы увидите незнакомую цель:

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

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

public static <T> @Nullable Optional<T> toJavaUtil
public static <T> [email protected] Optional<T> toJavaUtil

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

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

И обработал его, используя:

public [email protected](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
        }
    }
}

Я уверен, что многие полезные фреймворки, такие как checkerframework, наиболее полно используют ElementType.TYPE_USE