2011-11-24 2 views
4

J'ai une classe abstraite générique Factory<T> avec une méthode createBoxedInstance() qui retourne les instances de T créées par les implémentations de createInstance() enveloppés dans le conteneur générique Box<T>.usine générique de conteneurs génériques

abstract class Factory<T> { 
    abstract T createInstance(); 

    public final Box<T> createBoxedInstance() { 
     return new Box<T>(createInstance()); 
    } 

    public final class Box<T> { 
     public final T content; 

     public Box(T content) { 
      this.content = content; 
     } 
    } 
} 

À certains moments, je besoin d'un conteneur de type Box<S>S est un ancêtre de T. Est-il possible de faire lui-même createBoxedInstance() générique afin qu'il retournera les instances de Box<S>S est choisi par l'appelant? Malheureusement, définir la fonction comme suit ne fonctionne pas comme un paramètre de type ne peut pas être déclaré en utilisant le mot-clé super, seulement utilisé.

public final <S super T> Box<S> createBoxedInstance() { 
    return new Box<S>(createInstance()); 
} 

La seule alternative que je vois, est de rendre tous les lieux qui ont besoin d'une instance de Box<S> accepter Box<? extends S> qui fait assignable à S membre contenu du conteneur.

Existe-t-il un moyen de contourner cela sans re-boxer les instances de T dans des conteneurs de type Box<S>? (Je sais que je pouvais jeter le Box<T> à un Box<S> mais je me sentais très, très coupable.)

+0

Avez-vous essayé la classe abstraite Factory , puis utilisez uniquement T createInstance(), tout en utilisant S partout ailleurs? Ou peut-être une classe abstraite Factory ? – Luis

+0

Si vous voulez un 'Box ' pourquoi ne pas utiliser un 'Factory ' Le problème que vous avez est que même si S étend T vous ne pouvez pas connaître un 'Factoy ' crée un S en boîte, il pourrait être un boxed U (qui s'étend également T) –

+0

@Luis Le problème avec cela est que je dois décider à l'avance quel type je veux utiliser pour S. L'idée est que je peux créer une usine de T, puis l'utiliser pour créer en boîte T avec différents paramètres de type pour S. – Feuermurmel

Répondre

2

Essayez de réécrire votre autre code à ne pas utiliser Box<S> mais utilise plutôt Box<? extends S>, il acceptera une Box<T> , aussi. De cette façon, vous expliquez que la boîte peut également contenir des sous-classes de S.

Ce qui suit devrait également fonctionner, si vous faites votre boîte static:

public static <S, T extends S> Box<S> createBoxedInstance(Factory<T> factory) { 
    return new Box<S>(factory.createInstance()); 
} 

Cependant, il peut être incapable de faire l'inférence de type pour S, à quel point vous avez besoin:

public static <S, T extends S> Box<S> createBoxedInstance(Factory<T> factory, S dummy) { 
    return new Box<S>(factory.createInstance()); 
} 

Une autre variation qui est très explicite:

public static <S, T extends S> Box<? extends S> createBoxedInstance(Test<T> factory, S dummy) { 
    return factory.createBoxedInstance(); // Actually a Box<T> 
} 
+0

Ce sont tous des hacks cependant - vous n'obtenez pas de sémantique propre à partir de cela. Il est beaucoup plus judicieux d'utiliser 'Box ' pour reconnaître que la boîte peut être pour un sous-type. –

+0

Cette réponse contient le plus d'exemples qui répondent à ma question d'origine même si je l'accepte parce qu'il est conseillé de ne pas réellement vouloir une fonction qui convertit un 'Box ' en un 'Box ' en interne. 'Box ' est le chemin à parcourir. Regardez [la réponse de Ben Schulz] (http://stackoverflow.com/a/8280336/402281) si vous voulez réellement une solution à ma question originale, en utilisant une distribution non contrôlée en interne. – Feuermurmel

0

Vous pouvez ive ce que vous voulez dans une certaine mesure:

public final <S extends Box<? super T>> S createBoxedInstance() { 
    // sadly we can't capture the wildcard in the bound of S 
    // this means we have to cast, but at least it's local.. 
    // *should* this cast ever break due to reification, we 
    // can fix it here and be good to go 
    @SuppressWarnings("unchecked") 
    S box = (S) new Box<T>(createInstance()); 
    return box; 
} 

Factory<Integer> factory = getFactory(); 
Box<Number> box1 = factory.createBoxedInstance(); 
Box<Object> box2 = factory.createBoxedInstance(); 

Je pense que votre propre Box<? extends S> ALTERNATIVE est la bonne approche. Toutefois, si cela forcera code client à jonché de jokers, le ci-dessus pourrait être préférable.

+0

J'ai lié votre réponse dans les commentaires de la réponse acceptée, mais je suis d'accord avec votre conseil (et d'autres) pour aller avec «Box ». – Feuermurmel

0

La raison pour laquelle vous ne pouvez pas le faire (ou vous devez utiliser d'étranges conversions) est la suivante: Imaginez que le contenu de la boîte au lieu d'être final ait également un setter. En outre, supposons que le type S est le super type de R et de T. Le code suivant serait valide sans aucun casting.

Box<T> boxT = new Box<T>(objectT); 
Box<S> boxS = boxT; 
S objectR = new R(); 
boxS.set(objectR); 

Ce code serait valide sans avertissement, à l'exception du poseur échouerait à l'exécution avec une exception inattendue (inattendu dans le sens où il est évident qu'une exception coulée peut être jeté) parce que vous ne pouvez pas affecter quelque chose avec un de type R à quelque chose de type T.

vous pouvez en lire plus à http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#Topic3 (mais ont l'aspirine à proximité, vous aurez un mal de tête)

Comme beaucoup de gens ont dit, en utilisant probablement la boîte dans l'autre code est la meilleure option.

Questions connexes