Comment parcourir cette liste générique avec des caractères génériques?

J’ai une liste d’objects qui étendent une autre classe:

List arguments; 

Maintenant, je veux invoquer une méthode sur ces objects. La classe appelante a une méthode wash pour chacune des classes qui étendent Fruit , mais PAS pour la classe abstraite Fruit :

 void wash( Apple a); void wash( Peach p); 

Comment puis-je appliquer la méthode wash à tous les éléments des arguments ? Cela NE marche PAS, car mes méthodes de lavage n’acceptent pas les arguments de Fruit :

 for( Fruit f: arguments) this.wash( f); // the wash() method is not a member of Fruit 

Y at-il un moyen de résoudre ce problème sans avoir à faire une méthode de wash( Fruit) parapluie wash( Fruit) ? Parce qu’il existe des dizaines de méthodes de wash( ? extends Fruit)

.

EDIT : La “classe d’invocation” dont je parle est un visiteur. Et je ne peux modifier aucune des classes / sous-classes de Fruit . Je ne peux que programmer le visiteur. Cela signifie qu’il n’est pas possible d’append la méthode wash() (ni aucune autre méthode d’ailleurs) à la classe abstraite Fruit .

Bienvenue dans le monde de Double Dynamic Dispatch .

Autant que je sache, vous ne pouvez pas le faire facilement sur Java. Vous pouvez le faire de deux manières: la méthode quick’n’dirty et la méthode visiteur:

Quick’n’dirty

Vous devez demander le type de l’object, vous aurez donc besoin d’une méthode de lavage sur Fruit qui redirecta l’appel vers la fonction appropriée en fonction de son type:

 public void wash(Fruit f) { if(f instanceof Apple) { wash((Apple) f) ; } else if(f instanceof Peach) { wash((Peach) f) ; } else { // handle the error, usually through an exception } } 

Le problème avec quick’n’dirty est que le compilateur ne vous dira pas qu’il existe un nouveau fruit valide (par exemple, Orange) qui n’est actuellement pas géré par la méthode de lavage.

Visiteur

Vous pouvez utiliser le modèle de visiteur pour Fruit:

 public abstract class Fruit { // etc. public abstract void accept(FruitVisitor v) ; } public class Apple extends Fruit { // etc. public void accept(FruitVisitor v) { v.visit(this) ; } } public class Peach extends Fruit { // etc. public void accept(FruitVisitor v) { v.visit(this) ; } } 

Et définir le visiteur comme:

 public interface class FruitVisitor { // etc. // Note that there are no visit method for Fruit // this is not an error public void visit(Apple a) ; public void visit(Peach p) ; } 

Et puis, le visiteur pour votre trousse de canvastte:

 public class FruitVisitorWasher : implements FruitVisitor { // etc. // Note that there are no visit method for Fruit // this is not an error // Note, too, that you must provide a wash method in // FruitVisitorWasher (or use an anonymous class, as // in the example of the second edit to access the // wash method of the outer class) public void visit(Apple a) { wash(a) ; } public void visit(Peach p) { wash(p) ; } } 

En fin de compte, votre code pourrait être

 FruitVisitorWasher fvw = new FruitVisitorWasher() ; for( Fruit f: arguments) { f.accept(fvw) ; } 

Et voilà…

Le modèle Visitor a l’avantage que le compilateur vous indiquera si vous avez ajouté un autre fruit (par exemple, Orange) dans lequel vous avez codé une méthode d’acceptation et si vous avez oublié de mettre à jour le modèle FruitVisitor pour la prendre en charge.

Et puis, le motif Visiteur est extensible: vous pouvez avoir un FruitVisitorWasher, un FruitVisitorEater, un FruitVisitorWwhat, en les ajoutant sans avoir besoin de modifier Fruit ni Apple, Peach, etc.

Un piège, cependant, vous devez écrire manuellement dans chaque classe Fruit la méthode accept (qui est une action copier / coller) car c’est cette méthode qui effectue tout le travail de “connaissance” du type de fruit approprié.

modifier

Si vous optez pour la solution Quick’n’dirty, la solution de Samuel Parsonage pourrait être encore meilleure que la mienne:

Comment parcourir cette liste générique avec des caractères génériques?

qui utilise la reflection de Java (je suis un codeur C ++, la reflection n’est donc pas une solution naturelle … ma faute à ce sujet …). Je trouve sa solution tout à fait élégante, même si ça sent mauvais (toutes les vérifications seront effectuées au moment de l’exécution, alors vous feriez mieux de vous assurer que tout va bien … Encore une fois, en arrière-plan C ++: si quelque chose peut être fait, ou une erreur peut être détecté au moment de la compilation, évitez autant que possible de le déplacer au moment de l’exécution)

Modifier 2

John Assymptoth a commenté:

Le modèle de visiteur tel que vous l’écrivez n’est pas une option, car je ne peux pas append la méthode wash à Fruit.

Je vais donc proposer le code en ligne pour prouver que wash () n’est pas censé être à l’intérieur de Fruit pour fonctionner.

(J’ai changé FruitVisitor d’une classe abstraite en une interface, ce qui est mieux)

Imaginons que la boucle for soit à l’intérieur de la méthode bar de la classe Foo, qui possède sa propre méthode wash:

 public class Foo { public wash(Apple a) { /* etc. */ } public wash(Peach p) { /* etc. */ } public bar(List arguments) { for( Fruit f: arguments) { wash(f) ; // we wand the right wash method called. } } } 

Vous voulez que la bonne méthode de lavage soit appelée afin que le code ci-dessus ne fonctionne pas correctement.

Réutilisons le modèle FruitVisitor pour corriger ce code. Nous allons utiliser une classe anonyme dans la méthode bar:

 public class Foo { public void wash(Apple a) { System.out.println("Apple") ; } public void wash(Peach p) { System.out.println("Peach") ; } public void bar(List arguments) { FruitVisitor fv = new FruitVisitor() { public void visit(Apple a) { wash(a) ; // will call the wash method // of the outer class (Foo) } public void visit(Peach p) { wash(p) ; // will call the wash method // of the outer class (Foo) } } ; for(Fruit f: arguments) { f.accept(fv) ; } } } 

Maintenant, cela fonctionne, et il n’y a pas de méthode de lavage dans Fruits.

Notez que ce code a été testé sur une JVM 1.6, je peux donc fournir le code complet si nécessaire.

Ceci est possible en utilisant la reflection

Essaye ça

 Method m=this.getClass().getMethod("wash", f.getClass()); m.invoke(this, f.getClass().cast(f)); 

Essayez de modifier

 void wash( Apple a); 

à

 void wash(List list); 

Et utilisez ensuite le premier élément de la méthode de lavage.

Vous devez toujours avoir la méthode wash () dans la classe Fruit.

La méthode de lavage dans la classe Fruit sera abstraite et devrait être définie dans les sous-classes.