具有多个值类型的映射,具有泛型的优点 [英] Map with multiple value types with advantages of generics
问题描述
我想创建一个地图,它将提供泛型的好处,同时支持多种不同类型的值。我认为以下是通用集合的两个主要优点:
- 将错误的东西编译时间警告
- 在收集集合时无需投射
所以我想要的是一张地图: p>
- 它支持多个值对象,
- 检查输入到映射中的值
基本案例,使用是:
地图< MyKey,Object> map = new HashMap< MyKey,Object>();
//没有类型检查put();
map.put(MyKey.A,A);
map.put(MyKey.B,10);
//需要从get();
Object a = map.get(MyKey.A);
String aStr =(String)map.get(MyKey.A);
我找到一种方法来解决第二个问题,通过创建AbstractKey,与此键相关联的值的类:
public interface AbstractKey< K> {
}
public enum StringKey implements AbstractKey< String> {
A,B;
}
public enum IntegerKey实现AbstractKey< Integer> {
C,D;
}
我可以创建一个TypedMap,并覆盖put )方法:
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);
}
}
这允许以下操作:
TypedMap map = new TypedMap();
map.put(StringKey.A,A);
String a = map.get(StringKey.A);
但是,如果我输入错误的键值,我不会收到任何编译错误。相反,我在get()上得到一个运行时 ClassCastException
。
.put(StringKey.A,10); //为什么不会导致编译错误?
String a = map.get(StringKey.A); // throws ClassCastException
如果此.put()可能会导致编译错误,
作为当前第二好的,我可以得到运行时 ClassCastException
在put()方法中抛出。
//将此方法添加到AbstractKey接口:
public Class getValueClass();
//例如,在StringKey枚举实现中:
public class getValueClass(){
return String.class;
}
//并覆盖TypedMap中的put()方法:
public< K> K put(AbstractKey K key,K value){
Object v = key.getValueClass()。cast(value);
return(K)super.put(key,v);
}
现在, ClassCastException
被抛出时放入地图,如下。这是更好的,因为它允许更容易/更快的调试,以识别不正确的键/值组合已放入TypedMap中。
map.put(StringKey.A,10); //现在抛出一个ClassCastException
所以,我想知道:
- 为什么
map.put(StringKey.A,10)
导致编译错误? li>
-
如何调整此设计以在put上获取有意义的编译错误,其中的值不是关联的泛型类型?
-
这是一个合适的设计,以实现我想要的(见上)?
-
有没有其他设计可以用来达到我想要的效果?
EDIT - 说明:
- 如果你认为这是一个不好的设计 - 你能解释一下为什么吗?
- 我使用String和Integer作为示例值类型 - 在现实中,我有很多不同的键/值类型对,我想要能够使用。我想在单一地图中使用这些。这是目标。
与泛型和重载在一个坏的方式。你正在扩展 HashMap< AbstractKey,Object>
,因此你的类继承了方法 Object put(AbstractKey k,Object v)
。在你的类中,你定义了另一个具有不同签名的 put
方法,这意味着你只是重载 put
当您编写 map.put(StringKey.A,10)
时,编译器会尝试找到一个符合参数类型 put(StringKey,Integer)
的方法。您的方法的签名不适用,但继承的 put
的 - StringKey
与 AbstractKey
和 Integer
与 Object
兼容。因此,它将该代码编译为对 HashMap.put
的调用。
一种解决方法:rename put
添加到一些自定义名称,例如 typedPut
。
从经验,你的方法是非常有趣和有吸引力,但在现实生活中它只是不值得的麻烦。
I want to create a map that will provide the benefits of generics, whilst supporting multiple different types of values. I consider the following to be the two key advantages of generic collections:
- compile time warnings on putting wrong things into the collection
- no need to cast when getting things out of collections
So what I want is a map:
- which supports multiple value objects,
- checks values put into the map (preferably at compile-time)
- knows what object values are when getting from the map.
The base case, using generics, is:
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);
I've found a way to resolve the second issue, by creating an AbstractKey, which is generified by the class of values associated with this key:
public interface AbstractKey<K> {
}
public enum StringKey implements AbstractKey<String>{
A,B;
}
public enum IntegerKey implements AbstractKey<Integer>{
C,D;
}
I can then create a TypedMap, and override the put() and get() methods:
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);
}
}
This allows the following:
TypedMap map = new TypedMap();
map.put(StringKey.A, "A");
String a = map.get(StringKey.A);
However, I don't get any compile errors if I put in the wrong value for the key. Instead, I get a runtime ClassCastException
on get().
map.put(StringKey.A, 10); // why doesn't this cause a compile error?
String a = map.get(StringKey.A); // throws a ClassCastException
It would be ideal if this .put() could give a compile error.
As a current second best, I can get the runtime ClassCastException
to be thrown in the put() method.
// 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);
}
Now, the ClassCastException
is thrown when put into the map, as follows. This is preferable, as it allows easier/faster debugging to identify where an incorrect key/value combination has been put into the TypedMap.
map.put(StringKey.A, 10); // now throws a ClassCastException
So, I'd like to know:
- Why doesn't
map.put(StringKey.A, 10)
cause a compile error? How could I adapt this design to get meaningful compile errors on put, where the value is not of the associated generic type of the key?
Is this is a suitable design to achieve what I want (see top)? (Any other thoughts/comments/warnings would also be appreciated...)
Are there alternative designs that I could use to achieve what I want?
EDIT - clarifications:
- If you think this is a bad design - can you explain why?
- I've used String and Integer as example value types - in reality I have a multitude of different Key / value type pairs that I would like to be able to use. I want to use these in a single map - that's the objective.
You are messing with generics and overloading in a bad way. You are extending HashMap<AbstractKey, Object>
and so your class is inheriting the method Object put(AbstractKey k, Object v)
. In your class you are defining another put
method with a different signature, which means you are just overloading the put
method, instead of overriding it.
When you write map.put(StringKey.A, 10)
, the compiler tries to find a method that conforms to the argument types put(StringKey, Integer)
. Your method's signature doesn't apply, but the inherited put
's does -- StringKey
is compatible with AbstractKey
and Integer
is compatible with Object
. So it compiles that code as a call to HashMap.put
.
A way to fix this: rename put
to some custom name, like typedPut
.
BTW talking from experience your approach is very fun and engaging, but in real life it just isn't worth the trouble.
这篇关于具有多个值类型的映射,具有泛型的优点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!