Quelle est la signification de java.util. @ Nullable?

56

Je lis le code de Guava où j'ai trouvé l'annotation java.util.@Nullabledans du code. Je connais le sens de @Nullable, mais je ne comprends pas celui-ci. En particulier, je ne trouve pas de classe appelée Nullabledans le package java.util. S'il vous plaît, quelqu'un me dit quel est le sens de ceci java.util.@Nullable:

public static <T> java.util.@Nullable Optional<T> toJavaUtil(
    @Nullable Optional<T> googleOptional) {
  return googleOptional == null ? null : googleOptional.toJavaUtil();
}
SetDaemon
la source
4
@PaulRooney Je ne pense pas. Ce dont Nullableils parlent semble y être javax.annotation, non java.util.
Joseph Sible-Reinstate Monica
3
Je me demande si cela pourrait simplement être un étrange syntaxe Java. En particulier, cela pourrait-il simplement signifier public static <T> @Nullable java.util.Optional<T> toJavaUtil, mais que Java ne vous permet pas de l'écrire de cette façon?
Joseph Sible-Reinstate Monica
1
@JosephSible public static <T> @Nullable java.util.Optional<T> toJavaUtiln'est en effet pas possible, mais il n'y a aucune raison d'utiliser le FQN Optionallorsque l'importation existe déjà.
Tom
6
@ Tom Il y en a un autre Optionaldans le champ d'application.
Joseph Sible-Reinstate Monica
2
merci les gars, je l'ai trouvé dans 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

Réponses:

37

La ligne public static <T> java.util.@Nullable Optional<T> toJavaUtilest écrite comme ceci, car le style habituel public static <T> @Nullable java.util.Optional<T> toJavaUtiln'est pas valide. Ceci est défini dans le JLS §9.7.4 :

Il s'agit d'une erreur au moment de la compilation si une annotation de type T s'applique à un type (ou à toute partie d'un type) dans un contexte de type, et T est applicable dans des contextes de type, et l'annotation n'est pas admissible.

Par exemple, supposons un type d'annotation TA qui est méta-annoté avec just @Target(ElementType.TYPE_USE). Les termes @TA java.lang.Objectet java.@TA lang.Objectsont illégaux car le nom simple dont @TA est le plus proche est classé comme nom de package. D'un autre côté, java.lang.@TA Objectc'est légal.

La déclaration de type de org.checkerframework.checker.nullness.qual@Nullableest:

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

Cela s'applique donc à cette règle.

Le fait que cette structure ne casse pas l'exécution, puisque le nom du package java.utilet de la classe Optionalont été séparés, peut être vu lorsque nous regardons le code compilé en utilisant 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.Optionalest une classe locale dans laquelle j'ai copié le code de la goyave, afin d'obtenir un exemple minimal à dé / compiler)

Comme vous pouvez le voir, l'annotation n'existe plus. Ce n'est qu'un marqueur pour le compilateur pour empêcher un avertissement lorsque la méthode retourne null (et un indice pour les lecteurs de code source), mais il ne sera pas inclus dans le code compilé.


Cette erreur de compilation s'applique également aux variables telles que:

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

Mais peut devenir acceptable lorsque l'annotation obtient en outre le type cible ElementType.FIELD, comme écrit dans la même clause JLS:

Si TA est en outre méta-annoté avec @Target(ElementType.FIELD), alors le terme @TA java.lang.Objectest légal dans les emplacements qui sont à la fois des contextes de déclaration et de type, comme une déclaration de champ @TA java.lang.Object f;. Ici, @TA est réputé s'appliquer à la déclaration de f (et non au type java.lang.Object) car TA est applicable dans le contexte de déclaration de champ.

À M
la source
11

Lorsque vous utilisez des annotations, il s'agit de la syntaxe utilisée lorsque vous souhaitez écrire un nom complet pour le type, au lieu d'ajouter une instruction d'importation.

Citant le manuel du framework checker :

La syntaxe Java correcte pour écrire une annotation sur un nom de type complet consiste à placer l'annotation sur la partie de nom simple, comme dans java.util. @ Nullable List. Mais, il est généralement préférable d'ajouter import java.util.List à votre fichier source, afin que vous puissiez simplement écrire @Nullable List.

Il est également mentionné à la page 2 de la spécification JSR308 qui peut être téléchargée ici . Ça dit:

Une annotation de type apparaît avant le nom simple du type, comme dans @NonNull String ou java.lang. @ NonNull String.

Kartik
la source
6

Le plus étrange ici est vraiment la syntaxe inconnue pour appliquer une ElementType.TYPE_USEannotation ciblée. Si vous consultez les documents de Nullable , vous verrez la cible inconnue:

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

Cette annotation est utilisée juste avant le nom simple du type annoté, comme dans les deux cas suivants:

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

Je ne savais pas à quoi servait ce type de cible, donc après une lecture rapide, je suis arrivé à cet exemple simple, qui récupère les métadonnées de type retour en utilisant une annotation qui a cette cible:

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

Et je l'ai traité en utilisant:

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

Je suis sûr que de nombreux cadres utiles, tels que checkerframework, font l’utilisation la plus appropriée de ElementType.TYPE_USE

ernest_k
la source