Was bedeutet java.util. @ Nullable?

56

Ich lese den Code von Guava, wo ich die Anmerkung java.util.@Nullablein einem Code gefunden habe. Ich kenne die Bedeutung von @Nullable, aber ich verstehe diese nicht. Insbesondere kann ich keine Klasse finden, die Nullableim Paket aufgerufen wird java.util. Bitte, jemand sagt mir, was das bedeutet java.util.@Nullable:

public static <T> java.util.@Nullable Optional<T> toJavaUtil(
    @Nullable Optional<T> googleOptional) {
  return googleOptional == null ? null : googleOptional.toJavaUtil();
}
SetDaemon
quelle
4
@ PaulRooney Das glaube ich nicht. Das Nullable, worüber sie reden, scheint drin zu sein javax.annotation, nicht java.util.
Joseph Sible-Reinstate Monica
3
Ich frage mich, ob dies nur ein seltsamer Java-Syntax-Ismus sein könnte. Könnte dies im Grunde nur bedeuten public static <T> @Nullable java.util.Optional<T> toJavaUtil, aber dass Java es Ihnen einfach nicht erlaubt, es so zu schreiben?
Joseph Sible-Reinstate Monica
1
@JosephSible public static <T> @Nullable java.util.Optional<T> toJavaUtilist zwar nicht möglich, aber es gibt keinen Grund, den FQN zu verwenden, Optionalwenn der Import bereits vorhanden ist.
Tom
6
@ Tom Da ist noch ein anderer OptionalBereich.
Joseph Sible-Reinstate Monica
2
Danke Jungs, ich habe das in Checker Framework gefunden. @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

Antworten:

37

Die Zeile public static <T> java.util.@Nullable Optional<T> toJavaUtilist so geschrieben, weil der übliche Stil public static <T> @Nullable java.util.Optional<T> toJavaUtilungültig ist. Dies ist im JLS §9.7.4 definiert :

Es ist ein Fehler zur Kompilierungszeit, wenn eine Annotation vom Typ T für einen Typ (oder einen Teil eines Typs) in einem Typkontext gilt und T in Typkontexten anwendbar ist und die Annotation nicht zulässig ist.

Nehmen Sie beispielsweise einen Annotationstyp TA an, der mit just meta-annotiert ist @Target(ElementType.TYPE_USE). Die Begriffe @TA java.lang.Objectund java.@TA lang.Objectsind illegal, da der einfache Name, dem @TA am nächsten liegt, als Paketname klassifiziert wird. Auf der anderen Seite java.lang.@TA Objectist legal.

Die Typdeklaration von org.checkerframework.checker.nullness.qual@Nullablelautet:

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

Es gilt also für diese Regel.

Dass diese Struktur die Ausführung nicht unterbricht, da Paket- java.utilund Klassenname Optionalaufgeteilt wurden, zeigt sich am folgenden kompilierten Code 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.Optionalist eine lokale Klasse, in die ich den Guava-Code kopiert habe, um ein minimales Beispiel zum De- / Kompilieren zu erhalten)

Wie Sie sehen, existiert die Anmerkung dort nicht mehr. Es ist nur eine Markierung für den Compiler, um eine Warnung zu verhindern, wenn die Methode null zurückgibt (und ein Hinweis für die Quellcodeleser), aber sie wird nicht in den kompilierten Code aufgenommen.


Dieser Compilerfehler gilt auch für Variablen wie:

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

Kann jedoch akzeptabel werden, wenn die Annotation zusätzlich den Zieltyp erhält ElementType.FIELD, wie in derselben JLS-Klausel geschrieben:

Wenn TA zusätzlich mit Meta-Annotationen versehen ist @Target(ElementType.FIELD), ist der Begriff @TA java.lang.Objectan Orten zulässig, die sowohl Deklarations- als auch Typkontexte sind, z. B. eine Felddeklaration @TA java.lang.Object f;. Hier gilt @TA für die Deklaration von f (und nicht für den Typ java.lang.Object), da TA im Kontext der Felddeklaration anwendbar ist.

Tom
quelle
11

Wenn Sie Anmerkungen verwenden, ist dies die Syntax, die verwendet wird, wenn Sie einen vollständig qualifizierten Namen für den Typ schreiben möchten, anstatt eine Importanweisung hinzuzufügen.

Zitat aus dem Checker Framework Handbuch :

Die richtige Java-Syntax zum Schreiben einer Anmerkung zu einem vollständig qualifizierten Typnamen besteht darin, die Anmerkung wie in java.util. @ Nullable List in den einfachen Namensteil einzufügen. Normalerweise ist es jedoch besser, import java.util.List zu Ihrer Quelldatei hinzuzufügen, damit Sie einfach @Nullable List schreiben können.

Es wird auch auf Seite 2 der JSR308- Spezifikation erwähnt, die hier heruntergeladen werden kann . Es sagt:

Vor dem einfachen Namen des Typs wird eine Typanmerkung angezeigt, z. B. in @NonNull String oder java.lang. @ NonNull String.

Kartik
quelle
6

Das Seltsame hier ist wirklich die ungewohnte Syntax zum Anwenden einer ElementType.TYPE_USEzielgerichteten Annotation. Wenn Sie die Dokumente des Nullable überprüfen , sehen Sie das unbekannte Ziel:

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

Diese Anmerkung wird direkt vor dem einfachen Namen des mit Anmerkungen versehenen Typs verwendet, wie in beiden folgenden Fällen:

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

Ich wusste nicht, wie diese Art von Ziel verwendet wurde, und so kam ich nach einem kurzen Lesen zu diesem einfachen Beispiel, in dem Metadaten vom Rückgabetyp mithilfe einer Anmerkung mit diesem Ziel erfasst werden:

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

Und verarbeitet es mit:

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

Ich bin sicher, dass viele nützliche Frameworks, wie z. B. checkerframework, am besten geeignet sind ElementType.TYPE_USE

ernest_k
quelle