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

56

Я читаю код Guava, где я нашел аннотацию 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();
}
SetDaemon
источник
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
@ Там есть еще одна 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недопустим. Это определено в JLS § 9.7.4 :

Это ошибка времени компиляции, если аннотация типа 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), потому что TA применяется в контексте объявления поля.

Том
источник
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
        }
    }
}

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

ernest_k
источник