web-dev-qa-db-fra.com

Erreur lors de la définition d'une valeur nulle par défaut pour le champ d'une annotation

Pourquoi est-ce que j'obtiens une erreur "La valeur d'attribut doit être constante". N'est-ce pas null constant ???

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
    Class<? extends Foo> bar() default null;// this doesn't compile
}
69
ripper234

Je ne sais pas pourquoi, mais le JLS est très clair:

 Discussion

 Note that null is not a legal element value for any element type. 

Et la définition d'un élément par défaut est:

     DefaultValue:
         default ElementValue

Malheureusement, je continue de trouver que les nouvelles fonctionnalités de langage (énumérations et maintenant annotations) ont des messages d'erreur de compilation très inutiles lorsque vous ne répondez pas aux spécifications de langue.

EDIT: Un petit google a trouvé le suivant dans le JSR-308, où ils plaident pour autoriser les null dans cette situation:

Nous notons quelques objections possibles à la proposition.

La proposition ne rend rien possible qui n'était pas possible auparavant.

La valeur spéciale définie par le programmeur fournit une meilleure documentation que null, ce qui peut signifier "aucun", "non initialisé", null lui-même, etc.

La proposition est plus sujette aux erreurs. Il est beaucoup plus facile d'oublier de vérifier avec null que d'oublier de vérifier une valeur explicite.

La proposition peut rendre l'idiome standard plus verbeux. Actuellement, seuls les utilisateurs d'une annotation doivent vérifier ses valeurs spéciales. Avec la proposition, de nombreux outils qui traitent les annotations devront vérifier si la valeur d'un champ est nulle de peur de lever une exception de pointeur nul.

Je pense que seuls les deux derniers points sont pertinents pour "pourquoi ne pas le faire en premier lieu". Le dernier point soulève certainement un bon point - un processeur d'annotation n'a jamais à craindre qu'il obtienne une valeur nulle sur une valeur d'annotation. J'ai tendance à voir que c'est plus le travail des processeurs d'annotation et d'autres codes de framework de devoir faire ce genre de vérification pour rendre le code des développeurs plus clair que l'inverse, mais il serait certainement difficile de justifier de le changer.

57
Yishai

Essaye ça

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
    Class bar() default void.class;
}

Il ne nécessite pas de nouvelle classe et c'est déjà un mot-clé dans Java cela ne veut rien dire.

56
Logan Murphy

Il semblerait que ce soit illégal, bien que le JLS soit très flou à ce sujet.

J'ai épuisé ma mémoire pour essayer de penser à une annotation existante qui avait un attribut Class à une annotation, et je me suis souvenu de celui-ci de l'API JAXB:

@Retention(RUNTIME) @Target({PACKAGE,FIELD,METHOD,TYPE,PARAMETER})        
public @interface XmlJavaTypeAdapter {
    Class type() default DEFAULT.class;

    static final class DEFAULT {}    
}

Vous pouvez voir comment ils ont dû définir une classe statique factice pour contenir l'équivalent d'un null.

Désagréable.

49
skaffman

Il semble qu'il y ait une autre façon de procéder.

Je n'aime pas ça non plus, mais ça pourrait marcher.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
    Class<? extends Foo>[] bar() default {};
}

Donc, fondamentalement, vous créez un tableau vide à la place. Cela semble autoriser une valeur par défaut.

9
Shervin Asgari
Class<? extends Foo> bar() default null;// this doesn't compile

Comme mentionné, la spécification de langage Java Java n'autorise pas les valeurs nulles dans les valeurs par défaut des annotations.

Ce que j'ai tendance à faire, c'est de définir DEFAULT_VALUE constantes en haut de la définition d'annotation. Quelque chose comme:

public @interface MyAnnotation {
    public static final String DEFAULT_PREFIX = "__class-name-here__ default";
    ...
    public String prefix() default DEFAULT_PREFIX;
}

Ensuite, dans mon code, je fais quelque chose comme:

if (myAnnotation.prefix().equals(MyAnnotation.DEFAULT_PREFIX)) { ... }

Dans votre cas avec une classe, je définirais simplement une classe marqueur. Malheureusement, vous ne pouvez pas avoir de constante pour cela, vous devez donc faire quelque chose comme:

public @interface MyAnnotation {
    public static Class<? extends Foo> DEFAULT_BAR = DefaultBar.class;
    ...
    Class<? extends Foo> bar() default DEFAULT_BAR;
}

Votre classe DefaultFoo serait juste une implémentation vide pour que le code puisse faire:

if (myAnnotation.bar() == MyAnnotation.DEFAULT_BAR) { ... }

J'espère que cela t'aides.

2
Gray

Ce message d'erreur est trompeur, mais non, le littéral null n'est pas une expression constante, comme défini dans la spécification de langage Java, ici .

De plus, le Java Language Specification dit

Il s'agit d'une erreur de compilation si le type de l'élément n'est pas proportionné (§9.7) avec la valeur par défaut spécifiée.

Et pour expliquer ce que cela signifie

Il s'agit d'une erreur au moment de la compilation si le type d'élément n'est pas proportionné à la valeur de l'élément. Un type d'élément T est proportionnel à une valeur d'élément V si et seulement si l'une des conditions suivantes est vraie:

  • T est un type de tableau E[], et soit:
    • [...]
  • T n'est pas un type de tableau , et le type de V est compatible avec les affectations (§5.2) avec T, et :
    • Si T est un type primitif ou String, alors V est une expression constante (§15.28).
    • Si T est Class ou une invocation de Class (§4.5), alors V est un littéral de classe (§15.8.2).
    • Si T est un type d'énumération (§8.9), alors V est une constante d'énumération (§8.9.1).
    • V n'est pas nul .

Voici donc votre type d'élément, Class<? extends Foo>, n'est pas proportionnel à la valeur par défaut, car cette valeur est null.

1