2011-05-23 2 views
31

Supposons que deux interfaces:usine générique avec les classes d'implémentation inconnus

public interface FruitHandler<T extends Fruit> 
{ 
    setFruit(T fruit); 
    T getFruit(); 
} 

public interface Fruit 
{ 
} 

Maintenant, je veux une usine pour créer FruitHandlers (par exemple AppleHander, OrangeHandler, ...), mais le FruitHandlerFactory ne sait pas neccessary au sujet de la mise en œuvre classes des deux interfaces (comme dans java parameterized generic static factory). Le FruitHandlerFactory devrait fonctionner de cette façon (où OrangeHandler implémente FruitHandler et Orange outils Fruit):

FruitHandlerFactory fhf = new FruitHandlerFactory<OrangeHandler,Orange>(); 
OrangeHandler fh = fhf.create(); 
Orange orange = (Orange)fh.getFruit(); 

Cela devrait être l'usine:

public class FruitHandlerFactory<A extends FruitHandler, B extends Fruit> 
{ 
    public FruitHandler create() 
    { 
     FruitHandler<Fruit> fh = new A<B>(); //<--- ERROR 
     fh.setFruit(new B()); 
     return fh; 
    } 
} 

où je reçois cette erreur:

The type A is not generic; it cannot be parameterized with arguments <B> 

BTW: Est-il possible de rendre la méthode create() statique?

+2

Excellente question, et excellent extrait de code pour illustrer. – sje397

Répondre

39

Étant donné que les génériques en Java sont implémentés à l'aide de l'effacement, les informations de type FruitHandlerFactory ne seront pas disponibles au moment de l'exécution, ce qui signifie que vous ne pouvez pas instancier A (ou B) de cette manière.

Vous pouvez, cependant passer dans un objet Class du type correct pour contourner ceci:

public class FruitHandlerFactory<H extends FruitHandler<F>, F extends Fruit> { 
    final Class<H> handlerClass; 
    final Class<F> fruitClass; 

    public FruitHandlerFactory(final Class<H> handlerClass, final Class<F> fruitClass) { 
     this.handlerClass = handlerClass; 
     this.fruitClass = fruitClass; 
    } 

    public H create() throws InstantiationException, IllegalAccessException { 
     H handler = handlerClass.newInstance(); 
     handler.setFruit(fruitClass.newInstance()); 
     return handler; 
    } 
} 

Un inconvénient mineur est que vous devrez écrire trois fois les noms de type (1) si vous voulez instancier un FruitHandlerFactory:

FruitHandlerFactory fhf = new FruitHandlerFactory<OrangeHandler,Orange>(OrangeHandler.class, Orange.class); 

vous pouvez réduire quelque peu ce en produisant une méthode staticcreateFactory() sur votre FruitHandlerFactory:

static <H extends FruitHandler<F>, F extends Fruit> FruitHandlerFactory<H, F> createFactory(
     final Class<H> handlerClass, final Class<F> fruitClass) { 
    return new FruitHandlerFactory<H, F>(handlerClass, fruitClass); 
} 

et l'utiliser comme ceci:

FruitHandlerFactory fhf = FruitHandlerFactory.createFactory(OrangeHandler.class, Orange.class); 
+0

C'est incroyable ... :-) Merci pour cette réponse excellente et complète! – Thor

2

De this question:

Peut-être essayer cela?

public class FruitHandlerFactory<A extends FruitHandler<B>, B extends Fruit> 
Questions connexes