Qual è il significato di java.util. @ Nullable?


56

Sto leggendo il codice di Guava in cui ho trovato l'annotazione java.util.@Nullablein qualche codice. Conosco il significato di @Nullable, ma non capisco questo. In particolare, non riesco a trovare una classe chiamata Nullablenel pacchetto java.util. Per favore, qualcuno mi dica qual è il significato di questo java.util.@Nullable:

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

4
@PaulRooney Non credo. La Nullablestanno parlando ci sembra essere in javax.annotation, non è java.util.
Joseph Sible: ripristina Monica il

3
Mi chiedo se questo potrebbe essere solo un bizzarro sintassi Java. In particolare, questo potrebbe sostanzialmente significare public static <T> @Nullable java.util.Optional<T> toJavaUtil, ma Java non ti permette di scriverlo in quel modo?
Joseph Sible: ripristina Monica il

1
@JosephSible non public static <T> @Nullable java.util.Optional<T> toJavaUtilè effettivamente possibile, ma non vi è alcun motivo per utilizzare l'FQN di Optionalquando l'importazione esiste già.
Tom,

6
@ Tom Ne esiste un altro Optionalnell'ambito.
Joseph Sible: reintegrare Monica il

2
grazie ragazzi, l'ho trovato in 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

Risposte:


37

La riga public static <T> java.util.@Nullable Optional<T> toJavaUtilè scritta in questo modo, perché il solito stile public static <T> @Nullable java.util.Optional<T> toJavaUtilnon è valido. Questo è definito nel JLS §9.7.4 :

È un errore in fase di compilazione se un'annotazione di tipo T si applica a un tipo (o a qualsiasi parte di un tipo) in un contesto di tipo e T è applicabile in contesti di tipo e l'annotazione non è ammissibile.

Ad esempio, supponi un tipo di annotazione TA che è meta-annotato con just @Target(ElementType.TYPE_USE). I termini @TA java.lang.Objecte java.@TA lang.Objectsono illegali perché il nome semplice a cui @TA è più vicino è classificato come nome del pacchetto. D'altra parte, java.lang.@TA Objectè legale.

La dichiarazione del tipo di org.checkerframework.checker.nullness.qual@Nullableè:

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

Quindi si applica a questa regola.

Che questa struttura non interrompa l'esecuzione, dal momento che il java.utilnome del pacchetto e della classe Optionalsono stati divisi, si può vedere osservando il codice compilato usando 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è una classe locale in cui ho copiato il codice Guava, per ottenere un esempio minimo di de- / compilare)

Come puoi vedere, l'annotazione non esiste più lì. È solo un indicatore per il compilatore per impedire un avviso quando il metodo restituisce null (e un suggerimento per i lettori di codice sorgente), ma non verrà incluso nel codice compilato.


Questo errore del compilatore si applica anche a variabili come:

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

Ma può diventare accettabile quando l'annotazione ottiene anche il tipo di destinazione ElementType.FIELD, come scritto nella stessa clausola JLS:

Se TA è inoltre meta-annotato con @Target(ElementType.FIELD), allora il termine @TA java.lang.Objectè legale in posizioni che sono sia contesti di dichiarazione che di tipo, come una dichiarazione di campo @TA java.lang.Object f;. In questo caso, @TA è considerato applicabile alla dichiarazione di f (e non al tipo java.lang.Object) perché TA è applicabile nel contesto della dichiarazione di campo.


11

Quando si utilizzano le annotazioni, questa è la sintassi utilizzata quando si desidera scrivere un nome completo per il tipo, anziché aggiungere l'istruzione di importazione.

Citando dal manuale del framework checker :

La sintassi Java corretta per scrivere un'annotazione su un nome di tipo completo è quella di inserire l'annotazione sulla parte del nome semplice, come in java.util. @ Nullable List. Ma di solito è meglio aggiungere import java.util.List al file sorgente, in modo da poter scrivere semplicemente @Nullable List.

È anche menzionato a pagina 2 della specifica JSR308 che può essere scaricata qui . Dice:

Un'annotazione di tipo appare prima del nome semplice del tipo, come in @NonNull String o java.lang. @ NonNull String.


6

La cosa strana qui è davvero la sintassi sconosciuta per l'applicazione di ElementType.TYPE_USEun'annotazione mirata. Se controlli i documenti della Nullable , vedrai il bersaglio sconosciuto:

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

Questa annotazione viene utilizzata prima del semplice nome del tipo annotato, come in entrambi i seguenti:

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

Non sapevo quale fosse l'uso di questo tipo di target, quindi dopo una lettura veloce, sono arrivato a questo semplice esempio, che raccoglie i metadati del tipo di ritorno usando un'annotazione che ha quel target:

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

Ed elaborato utilizzando:

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

Sono sicuro che molti framework utili, come checkerframework, ne facciano un uso più appropriato ElementType.TYPE_USE

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.