java

¿Cuál es el significado de java.util. @ Nullable?



(3)

Estoy leyendo el código de Guava donde encontré la anotación java.util.@Nullable en algún código. Sé el significado de @Nullable , pero no entiendo este. En particular, no puedo encontrar una clase llamada Nullable en el paquete java.util . Por favor, que alguien me diga cuál es el significado de este java.util.@Nullable :

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


Al usar anotaciones, esta es la sintaxis que se usa cuando desea escribir un nombre completo para el tipo, en lugar de agregar una declaración de importación.

Citando del manual del marco del verificador :

La sintaxis correcta de Java para escribir una anotación en un nombre de tipo totalmente calificado es colocar la anotación en la parte del nombre simple, como en java.util. @ Nullable List. Pero, por lo general, es mejor agregar import java.util.List a su archivo fuente, para que pueda simplemente escribir @Nullable List.

También se menciona en la página 2 de la especificación JSR308 que se puede descargar here . Dice:

Aparece una anotación de tipo antes del nombre simple del tipo, como en @NonNull String o java.lang. @ NonNull String.


La línea public static <T> java.util.@Nullable Optional<T> toJavaUtil se escribe así, porque el estilo habitual public static <T> @Nullable java.util.Optional<T> toJavaUtil no es válido. Esto se define en el JLS §9.7.4 :

Es un error en tiempo de compilación si una anotación de tipo T se aplica a un tipo (o cualquier parte de un tipo) en un contexto de tipo, y T es aplicable en contextos de tipo, y la anotación no es admisible.

Por ejemplo, suponga un tipo de anotación TA que se @Target(ElementType.TYPE_USE) con solo @Target(ElementType.TYPE_USE) . Los términos @TA java.lang.Object y java.@TA lang.Object son ilegales porque el nombre simple al que @TA está más cerca se clasifica como un nombre de paquete. Por otro lado, java.lang.@TA Object es legal.

La declaración de tipo de org.checkerframework.checker.nullness.qual@Nullable es:

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

Entonces se aplica a esta regla.

Que esta estructura no interrumpa la ejecución, ya que el paquete java.util y el nombre de clase Optional se dividieron, se puede ver cuando miramos el código compilado 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 es una clase local donde copié el código de Guava para obtener un ejemplo mínimo para descompilar / compilar)

Como puede ver, la anotación ya no existe. Es solo un marcador para el compilador para evitar una advertencia cuando el método devuelve nulo (y una pista para los lectores de código fuente), pero no se incluirá en el código compilado.

Este error del compilador también se aplica a variables como:

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

Pero puede volverse aceptable cuando la anotación adicionalmente obtiene el tipo de destino ElementType.FIELD , como está escrito en la misma cláusula JLS:

Si TA se @Target(ElementType.FIELD) adicionalmente con @Target(ElementType.FIELD) , entonces el término @TA java.lang.Object es legal en ubicaciones que son contextos de declaración y tipo, como una declaración de campo @TA java.lang.Object f; . Aquí, se considera que @TA se aplica a la declaración de f (y no al tipo java.lang.Object) porque TA es aplicable en el contexto de declaración de campo.


Lo extraño aquí realmente es la sintaxis desconocida para aplicar una anotación dirigida a ElementType.TYPE_USE . Si revisas los documentos de Nullable , verás el objetivo desconocido:

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

Esta anotación se usa justo antes del nombre simple del tipo anotado, como en los dos siguientes:

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

No sabía cuál era el uso de este tipo de objetivo, así que después de una lectura rápida, llegué a este ejemplo simple, que recoge los metadatos del tipo de retorno utilizando una anotación que tiene ese objetivo:

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

Y lo procesó usando:

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

Estoy seguro de que muchos marcos útiles, como checkerframework, hacen el uso más apropiado de ElementType.TYPE_USE