Java génériques et modèles de conception: non une référence à paramétrer un type générique est toujours une mauvaise chose?

StackOverflow https://stackoverflow.com/questions/7327209

Question

cette question est liée en partie à ma dernière question .

J'ai une classe générique qui représente une collection d'objets génériques:

public interface MyObject<N extends Number>{}

public interface MyCollecion<N extends Number> {
    public foo(MyObject<N> obj)
}

Dans ma conception de ces collections d'objets sont construits par une classe client (Appelons-le CollectionBuilder) à travers une approche de classe abstraite:

public interface AbstractCollectionFactory {
    public abstract <N extends Number> MyCollection<MyObject<N>> createCollection(String collectionType);

}

collections génériques doit être construite de cette façon:

public class CollectionBuilder{
    AbstractCollectionFactory f;
    public void buildDoubleCollection(){

         MyCollection<MyObject<Double>> c = f.<Double>createCell("MyType");
    }
    public void buildIntegerCollection(){...}
}

Ok.Since ici tous ok. CollectionBuilder est au courant de ce qui est le type de béton générique pour spécifier (double dans ce cas) et peut construire correctement. Je peux compiler ce sans avertissement et tout devrait fonctionner correctement.

Maintenant, j'ai une question liée à la fois aux médicaments génériques et la conception de mon programme.

J'ai une autre classe dans mon application qui ont besoin d'utiliser la collection construite par CollectionBuilder (Appelons cette userclass classe). Le UserClass:

  1. n'a pas besoin de savoir de quel type de béton particulier sont sa collection (MyCollection<MyObject<Double>> ou MyCollection<MyObject<Integer>>).
  2. effectuer certaines opérations sur ces collections certaines méthodes définies invoquant dans l'interface MyCollection.
  3. Je ne voudrais pas ajouter un type générique à elle.

Dans la situation décrite, est qu'une mauvaise idée ne paramètrer pas la MaCollection de classe générique insied UserClass?

public UserClass{
     MyCollection a;  //warning 
     MyCollection<?> b; //no warning

     public method(MyObject o){  //warning
          a.foo(b); //compile       
     }
     public method2(MyObject<?> o){
          b.foo(o); //error do not compile
     }
}

compilateur Java protestera toujours avec un avertissement si je ne précise pas le paramètre générique. Quoi qu'il en soit à l'intérieur UserClass Je ne sais pas le paramètre concret (et je voudrais ne sais pas) et déclarant même avec « ? » ne me permet pas d'appeler la méthode foo sur MyCollection.

La question est:

  1. pas une référence à paramétrer un type générique est toujours une mauvaise chose?
  2. si la réponse à 1 est NON, est-ce une bonne situation pour ne pas le faire?
  3. si la réponse est OUI à 1, comment puis-je utiliser la méthode MyCollection à l'intérieur UserClass sans connaître leurs types génériques?
  4. Le mien est une mauvaise conception?

J'espère avoir été clair. Je suis en train de lutter sur ce problème de jours, et je ne sais pas. S'il vous plaît aidez-moi.

Était-ce utile?

La solution

  1. Selon le compilateur Java, oui. Et cette réponse contient beaucoup de bons points pour expliquer pourquoi. Personnellement, je suis en désaccord dans certaines situations, en particulier lorsque l'utilisation de Class est concerné (comme par exemple, si vous avez un Map<Class, Object>). Dans ce cas, avoir à toujours amure <?> à la fin de « classe » se sent comme l'ennui inutile, et ne fait pas le code plus lisible.

    L'idée d'avoir Class être paramétrées en fonction de quel type d'objet auquel il est associé a toujours senti un peu douteux en premier lieu, pour moi. Le point de Class est d'avoir une interface commune qui peut être utilisée pour obtenir des informations sur un objet quelconque (ala réflexion), quel que soit son type.

    Mais je digresse. Selon le compilateur Java et les types génériques de implementors, vous devriez toujours paramétrer votre référence, même si vous utilisez uniquement <?>.

  2. Non applicable. Bien que vous pouvez toujours ne déclarera aucune information de type, puis utilisez @SuppressWarnings("unchecked") avec un casting explicite pour rendre le plaisir du compilateur.

  3. Voir la réponse de Bohême.

  4. Il y a pas assez d'information vraiment dire. Bien que le dessus de ma tête, je ne suis pas sûr de ce que l'utilisation cas réel serait d'avoir « une classe générique qui représente une collection d'objets génériques ». Pourquoi ne pas simplement utiliser les collections directement?

Autres conseils

Il y a presque toujours un moyen de paramétrer votre code. En bref, je serais en train de taper UserClass, comme ceci:

public UserClass <N extends Number> {
     MyCollection<N> c;

     public method(MyObject<N> o){
          c.foo(o);      
     }
} 

Modifier Si vous êtes inquiet au sujet poluting votre code client avec les génériques, vous pouvez faire une classe abstraite typée et fournir des implémentations non typé, comme ceci:

public abstract UserClass <N extends Number> {
     MyCollection<N> c;

     public method(MyObject<N> o){
          c.foo(o);      
     }
} 

public class DoubleUserClass extends UserClass<Double> {}

Pour réduire la classe ballonnement, vous pouvez mettre les classes non typé dans la classe de base:

public abstract UserClass <N extends Number> {
     MyCollection<N> c;

     public method(MyObject<N> o){
          c.foo(o);      
     }

     public static class DoubleImpl extends UserClass<Double> {}
     public static class FloatImpl extends UserClass<Float> {}
     public static class IntegerImpl extends UserClass<Integer> {}
} 

Et les utiliser comme ceci: new UserClass.DoubleImpl() etc

Il est facile de se laisser emporter avec les génériques. Vous devez arrêter quelque part. Donc non, je ne pense pas que pas une référence à paramétrer un type générique est toujours une mauvaise chose. Parfois, l'effort supplémentaire nécessaire pour obtenir tous les médicaments génériques exactement droit est tout simplement pas la peine.

Cependant, il est préférable de spécifier génériques lorsque cela est possible, bien sûr. vous pouvez dans votre exemple, ajouter un paramètre générique UserClass comme le suggère Bohême.

Quant à savoir si votre collection est une mauvaise conception: les interfaces / infrastructure que vous définissez semble ici trop général. Qu'est-ce que MyCollection a qui ne peut être fait avec simplement java.util.List<YourClass>? Est-il vraiment besoin d'une interface MyObject? Si vous voulez « étiquette » un certain type de classes, vous pouvez certainement trouver une description plus précise de ce qui rend ces objets différents de tout autre Object?

 MyCollection<?> b; 

 public method2(MyObject<?> o){
      b.foo(o); 
 }

S'il y a une garantie hors bande qui b.foo(o) est sûr, nous devons faire ce travail d'appel. Introduire une méthode d'assistance

<N extends Number>
void foo(MyCollection<N> collection, MyObject<?> obj)
{
    @SuppressWarnings("unchecked")
    MyObject<N> obj2 = (MyObject<N>)obj;

    collection.foo(obj2);
}

MyCollection<?> b;
void method2(MyObject<?> o){
     foo(b, o); // now works
}

Le casting de MyObject<?> à MyObject<N> est sûr, en raison de la gaurantee hors bande. Il est un vérifié jeté dans ce sens, donc nous sommes justifiés de supprimer l'avertissement.

Dans une application réelle du monde, il est pas rare que nous en savons plus sur les relations de type que la langue peut exprimer; pas besoin d'être programmeur apologétique s'il en sait plus sur son code que le compilateur.

Castings impliquant génériques est un peu délicat; ce n'est pas intuitive comment fonctionne foo(b,o).

Il est bon d'utiliser MyCollection, MyObject types premières pour éviter les tracas. Ignorer l'avertissement que vous ne pouvez pas utiliser le type brut. C'est un non-sens.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top