web-dev-qa-db-fra.com

Java génériques et varargs

J'aimerais mettre en œuvre une fonction avec des génériques et des varargs.

public class Question {
    public static <A> void doNastyThingsToClasses(Class<A> parent, Class<? extends A>... classes) {
        /*** something here ***/
    }
    public static class NotQuestion {
    }
    public static class SomeQuestion extends Question {
    }
    public static void main(String[] args) {
        doNastyThingsToClasses(Object.class, Question.class, SomeQuestion.class); // OK
        doNastyThingsToClasses(Question.class, SomeQuestion.class); // OK
        doNastyThingsToClasses(Question.class, Object.class, SomeQuestion.class); // compilation failure
    }
}

L'intention ici est d'affirmer que tous les paramètres passés à cette fonction sont des objets de classe s'étendant la classe donnée en tant que premier paramètre. Donc, les deux premières lignes de la méthode principale compileraient et le 3ème génère une erreur.

Ma question est: Pourquoi je reçois "Sécurité type: une gamme générique de classe est créée pour un paramètre Varargs" Message pour les deux premières lignes?

Est-ce que j'ai râté quelque chose?

Question supplémentaire: Comment la redéfinir pour empêcher cet avertissement d'être affiché sur chaque ligne appelant "Donasthingstoclasses"? Je peux le changer en "donasthingthorstoclasses (classe <a> parent, classe <?> ... ... classes)" Et éloignez-vous des avertissements, mais cela supprime également le type de type de compilation - - pas si bon si je voulais Assurer le bon usage de cette fonction. Une meilleure solution?

32
Chris

Comme presque toujours, Angelika Langer's Java génériques FAQ explique-le en détail . (Faites défiler jusqu'à "Pourquoi le compilateur émet parfois-t-il parfois Un avertissement non coché lorsque j'invoque une méthode "Varargs"? "- L'ID ne fonctionne pas bien.)

Fondamentalement, vous finissez par perdre des informations d'une manière pire que la normale. Encore un autre petit point de douleur dans Java génériques :(

38
Jon Skeet

La réponse de Jon Skeet est (bien sûr) correcte; Je vais nous étendre un peu en soulignant que vous pouvez vous débarrasser de cet avertissement, avec un gros "si". Vous pouvez éviter cet avertissement si vous êtes prêt à vous engager à utiliser votre projet en utilisant Java 7.

Bob Lee a écrit ne proposition Pour que cet avertissement soit supprimé sur le site de déclaration de méthode, plutôt que sur le site d'utilisation, dans le cadre de Pièce de projet .

Cette proposition a été acceptée pour JDK7 (bien que la syntaxe a légèrement changé, à @SuppressWarnings("varargs")); Vous pouvez, si vous êtes curieux, regardez le commit qui a ajouté ce support à la JDK .

Pas nécessairement utile pour vous, mais je pensais faire une réponse séparée pour que cela vit pour les futurs lecteurs, qui pourraient avoir la chance de vivre dans un monde post-Java-7.

13
Cowan

De côté, l'avertissement peut maintenant être supprimé avec Java New @safevarargs Annotation.

@SafeVarargs
public static <A> void func( Class<A> parent, Class<? extends A>... classes ) {
    // Do func...
}
9
Scott

Ma solution à ce problème était de

  1. créer une classe nitaire
  2. supprimer ... de DonastothingSoclasses
  3. faire de donasthingstoclasses Aucun méthode statique
  4. faire le nom court, comme faire
  5. renvoyer ceci
  6. déplacer des arguments répétitifs sur les propriétés de la classe

    class Nastier {
      private final Class<A> parent;
    
      public Nastier(Class<A> parent) {
         this.parent = parent;
      }
    
      public <A, C extends A> Nastier do(Class<? extends A> clazz) {
         System.out.println(clazz);
         return this;
      }
    }
    
    public static void main(String[] args) {   
      Nastier nastier = new Nastier(Object.class);
      nastier.do(Question.class).do(SomeQuestion.class).do(NotQuestion.class);
    }
    

Je crois que le code a l'air propre et je suis heureux .... :)

5
LuisKarlos

Ok, alors finalement je finis par jeter la varargs loin:

public class Question {

    public static <A, C extends A> void doNastyThingsToClasses(Class<A> parent, List<Class<? extends A>> classes) {
        /******/
        for(Class<? extends A> clazz : classes) {
            System.out.println(clazz);
        }
    }

    public static class NotQuestion {
    }
    public static class SomeQuestion extends Question {
    }

    public static void main(String[] args) {

        ArrayList<Class<? extends Object>> classes = new ArrayList<Class<? extends Object>>();
        classes.add(Question.class);
        classes.add(SomeQuestion.class);
        classes.add(NotQuestion.class);
        doNastyThingsToClasses(Object.class, classes);

        ArrayList<Class<? extends Question>> clazzes = new ArrayList<Class<? extends Question>>();
        clazzes.add(Question.class);
        clazzes.add(SomeQuestion.class);
        clazzes.add(NotQuestion.class); // yes, this will _not_ compile
        doNastyThingsToClasses(Question.class, clazzes);

    }

}

La seule faille est le long code permettant de remplir la collection utilisée pour transporter les arguments de la fonction.

1
Chris

Le deuxième argument Class<? extends A> ... qui doit prolonger la classe que le premier argument est (ex. Argument one est un Question donc le deuxième argument soit quelque chose qui s'étend Question.

La panne:
NastyThingsToClasses(Object.class, Question.class, SomeQuestion.class); // OK
[.____] Tout s'étend Object donc le deuxième argument est correct.

NastyThingsToClasses(Question.class, SomeQuestion.class); // OK
SomeQuestion s'étend Question donc jeu équitable.

NastyThingsToClasses(Question.class, Object.class, SomeQuestion.class);
[.____] Object ne prolonge pas Question d'où une erreur.


[.____], j'espère que cela a effacé les choses.

-Brett

0
Brett