Carte avec plusieurs types de valeur avec des avantages de génériques
Je veux créer une carte qui vous offrent tous les avantages des médicaments génériques, tout en soutenant plusieurs différents types de valeurs. Je considère être les deux principaux avantages de générique collections:
- moment de la compilation des avertissements sur la mise mauvaises choses dans la collection
- pas besoin de cast lors de l'obtention des choses hors de collections
Donc, ce que je veux, c'est une carte:
- qui prend en charge plusieurs objets de valeur,
- vérifie les valeurs de mettre dans la carte (de préférence au moment de la compilation)
- sait ce que les valeurs de l'objet sont lors de l'obtention de la carte.
Le cas de base, l'utilisation de génériques, est:
Map<MyKey, Object> map = new HashMap<MyKey, Object>();
//No type checking on put();
map.put(MyKey.A, "A");
map.put(MyKey.B, 10);
//Need to cast from get();
Object a = map.get(MyKey.A);
String aStr = (String) map.get(MyKey.A);
J'ai trouvé un moyen de résoudre le deuxième problème, par la création d'un AbstractKey, qui est generified par la classe de valeurs associées à cette clé:
public interface AbstractKey<K> {
}
public enum StringKey implements AbstractKey<String>{
A,B;
}
public enum IntegerKey implements AbstractKey<Integer>{
C,D;
}
Je peux alors créer un TypedMap, et remplacer le put() et get() méthodes:
public class TypedMap extends HashMap<AbstractKey, Object> {
public <K> K put(AbstractKey<K> key, K value) {
return (K) super.put(key, value);
}
public <K> K get(AbstractKey<K> key){
return (K) super.get(key);
}
}
Cela permet à l'suivantes:
TypedMap map = new TypedMap();
map.put(StringKey.A, "A");
String a = map.get(StringKey.A);
Cependant, je n'ai pas toutes les erreurs de compilation si je l'ai mis dans la mauvaise valeur pour la clé. Au lieu de cela, je reçois un runtime ClassCastException
sur get().
map.put(StringKey.A, 10); //why doesn't this cause a compile error?
String a = map.get(StringKey.A); //throws a ClassCastException
L'idéal serait si ce .put() pourrait donner une erreur de compilation.
En tant que meilleur deuxième, je peux obtenir l'exécution ClassCastException
d'être jeté dans le mis() la méthode.
//adding this method to the AbstractKey interface:
public Class getValueClass();
//for example, in the StringKey enum implementation:
public Class getValueClass(){
return String.class;
}
//and override the put() method in TypedMap:
public <K> K put(AbstractKey<K> key, K value){
Object v = key.getValueClass().cast(value);
return (K) super.put(key, v);
}
Maintenant, le ClassCastException
est levée lors de la mettre dans la carte, comme suit. C'est préférable, car elle permet de plus facile/rapide de débogage pour identifier d'où une mauvaise clé/valeur de la combinaison a été mis dans le TypedMap.
map.put(StringKey.A, 10); //now throws a ClassCastException
Donc, j'aimerais savoir:
- Pourquoi ne pas
map.put(StringKey.A, 10)
provoquer une erreur de compilation? -
Comment pourrais-je adapter cette conception pour obtenir des erreurs de compilation, mis de, là où la valeur n'est pas associé au type générique de la clé?
-
Est que c'est une conception adaptée pour atteindre ce que je veux (voir plus haut)? (Toutes les autres idées/commentaires/avertissements serait également appréciée...)
-
Existe t il d'autres modèles que je pourrais utiliser pour obtenir ce que je veux?
MODIFIER précisions:
- Si vous pensez que c'est une mauvaise conception - pouvez-vous expliquer pourquoi?
- J'ai utilisé de la Ficelle et Entier comme exemple de types de valeur - en réalité, j'ai une multitude de Clé /valeur de type paires que je voudrais être en mesure d'utiliser. Je veux utiliser ces dans une seule carte - c'est l'objectif.
OriginalL'auteur amaidment | 2012-05-03
Vous devez vous connecter pour publier un commentaire.
Vous êtes de jouer avec de génériques et de la surcharge dans une mauvaise voie. Vous étendez
HashMap<AbstractKey, Object>
et donc votre classe hérite de la méthodeObject put(AbstractKey k, Object v)
. Dans votre classe, vous définissez une autreput
méthode avec une signature différente, ce qui signifie que vous êtes juste à la surcharge de laput
méthode, au lieu de passer outre.Lorsque vous écrivez
map.put(StringKey.A, 10)
, le compilateur essaie de trouver une méthode qui respecte les types d'argumentput(StringKey, Integer)
. Votre signature de la méthode ne s'applique pas, mais la héritéesput
't --StringKey
est compatible avecAbstractKey
etInteger
est compatible avecObject
. Donc, il compile ce code comme un appel àHashMap.put
.Un moyen de résoudre ce problème: renommer
put
à certains nom personnalisé, commetypedPut
.BTW parler de l'expérience de votre approche est très amusant et attachant, mais dans la vraie vie, il n'est tout simplement pas la peine.
J'ai confondu
put
avecSet.add
. Mais le point principal est toujours le même.vous mettez() la méthode n'hérite pas de la Carte.put (), Le compilateur voit deux méthodes ici: Carte.mettre(AbstractKey key, Object obj) et TypedMap.mettre(AbstractKey<K> clé K de la valeur). Essayez vous de la mise en œuvre sur netbeans, il ne vous montrera pas la remplacer indicateur d'annotation
Désolé - mais quel est votre point?
Je vois. Vous pensez que votre méthode ne substituez la méthode de la superclasse? L'annoter avec
@Override
à vérifier. Vous étendezHashMap<AbstractKey, Object>
et la définition de<K> K put(AbstractKey<K> key, K value)
. Ce n'est pas la même signature de la méthode queObject put(AbstractKey key, Object value)
.OriginalL'auteur Marko Topolnik
Article 29: Envisager de typesafe hétérogène des conteneurs.—Joshua Bloch, Efficace Java, Deuxième Édition, Chapitre 5: les Génériques.
Vous êtes les bienvenus; voir aussi de la Classe des Littéraux de Runtime-Jetons de Type.
OriginalL'auteur trashgod
À mon humble avis, chaque problème vient de la conception d'origine de l'odeur: vouloir mettre des valeurs de types différents dans la carte. Je enveloppez votre Entier et Chaîne de valeurs dans une commune
Value
type à la place. Quelque chose comme ceci:De cette façon, vous avez juste besoin d'un
Map<SomeKey, Value>
, et vous pouvez obtenir la valeur de la carte:En mettant les valeurs qui sont de types différents dans la carte signifie que vous devez utiliser instanceof sur les valeurs à faire quelque chose avec ces valeurs. Un plus OO façon de faire serait d'avoir une interface commune ou de la super-classe de toutes les valeurs possibles dans la carte, et d'utiliser cette interface commune ou de la super-classe comme la carte du type de la valeur. Si ce n'est pas possible, cela signifie probablement que vous devez utiliser plusieurs cartes.
Aussi - je ne pense pas que cette solution aide vraiment. Tout ce que vous avez fait est trouvé une autre façon de déterminer le type de la valeur de la carte. Je dirais même que c'est inférieur à mon approche, vous devez savoir quel Type de Valeur sera de sorte que vous pouvez ensuite coulé. Je ne vois pas ce que cela ne fait rien pour assurer la sécurité lors de la mise en carte.
Mais je n'ai pas à faire instanceof, car le type d'objet de valeur, je sors est déterminé par le générique de AbstractKey - c'est le point essentiel de ma conception.
Il a amélioré la sécurité de type, parce que les seules choses possibles qui peuvent être mis dans la carte de Valeur sont des instances. Et ceux qui ne peuvent envelopper d'une Chaîne ou un Entier. Dans votre question, vous démarrez à partir d'une Map<Maclé, Object>, qui peut contenir n'importe quel type d'Objet. Vous devriez peut-être nous expliquer, à un niveau plus élevé, pourquoi vous avez besoin pour stocker des Entiers, et de la Chaîne de valeurs dans une carte unique. Si vous avez deux cartes (une pour les valeurs entières, et un pour les valeurs de Chaîne, vous n'auriez pas à créer une classe spécifique.
OriginalL'auteur JB Nizet
Considérez ceci: (Grâce à l'efficacité de java)
Cette générer erreur de compilation
Cela ressemble juste à une dérivation de Marko réponse, sauf que depuis TypedMap ne s'étendent pas à la Carte, il est possible d'avoir put() et get() méthodes (plutôt que typedPut()). Cependant, je tiens à étendre la Carte - partie, alors que je reçois les fonctionnalités d'une Carte (itérateurs etc.), et, en partie, de sorte qu'il peut être manipulé comme une Carte (par exemple, dans d'autres moteurs de rendu personnalisé). Je ne suis pas sûr de ce que la valeur ajoutée est...?
OriginalL'auteur maress