以下代码显示我可以在Map中插入不兼容的类型,但是当我无法从中检索元素时。在下面的例子中,我可以将两个整数放入Map中,但如果我取消注释最后两行,我将得到ClassCastException。 这是JDK的错误,或者我错过了什么,因为我记得Java通用保证我们不能将不兼容的类型插入到泛型集合类中。
公共类HelloWorld {
private static class MapResultExtractor<K, V> {
public Map<K, V> extract(Iterator<List<Object>> iter)
throws IOException {
Map<K, V> map = new HashMap<K, V>();
while (iter.hasNext()) {
List<Object> tuple = iter.next();
K key = (K) (tuple.get(0) == null ? null : tuple.get(0));
V value = (V) (tuple.get(1) == null ? null : tuple.get(1));
map.put(key, value);
}
return map;
}
}
public static void main(String[] args) throws IOException {
MapResultExtractor<String, Integer> extractor = new MapResultExtractor<String, Integer>();
List<Object> subList = new ArrayList<Object>();
subList.add(1);
subList.add(2);
List<List<Object>> list = new ArrayList<List<Object>>();
list.add(subList);
Map<String, Integer> map = extractor.extract(list.iterator());
for (Map.Entry<String, Integer> entry : map.entrySet()) {
// System.out.println(entry.getKey().getClass() + "\t"
// + entry.getValue().getClass());
}
}
}
答案 0 :(得分:10)
编译器无法在此处查看
K key = (K) (tuple.get(0) == null ? null : tuple.get(0));
你真的传递了K
类型的对象(你真的传递了Integer而不是String)。
所以编译器信任你。
在运行时级别有类型擦除,所以当该行执行时,没有K
,而是
Object key = (tuple.get(0) == null ? null : tuple.get(0));
只有这样你才真正尝试在println
中使用Integer值而不是String,运行时可以检测到类型不匹配。
解决方案?使用Iterator<List<K>>
代替Iterator<List<Object>>
作为extract()
方法的参数(那么,在当前版本中,您将被迫返回Map<K, K>
而不是Map<K, V>
},这就是重点。
同样,代码的问题在于您强制将Integer视为Object(这是合法的)并强制将Object转换为类型K(在编译期间始终是合法的,并且在运行时并不总是正确的。)
答案 1 :(得分:0)
这是因为它的出口是
Map.Entry <String,Integer>
但事实上它是
Map.Entry<Integer,Integer>
编译器无法确保传递给extract方法的子列表包含或不包含K / V类型的对象。这样它在编译时就不会失败,但它建议你在21和22行发出一个未经检查的强制警告。
如果你想成为编译时错误,可以用这种方式声明提取方法
public Map<K, K> extract(Iterator<List<K>> iter) throws IOException
(我重复K,因为List只有一个参数类型)
这样你不需要演员:K key = (tuple.get(0) == null ? null : tuple.get(0));
K value = (tuple.get(1) == null ? null : tuple.get(1));
主要方法
MapResultExtractor<Integer, Integer> extractor =
new MapResultExtractor<Integer, Integer>();
如果您尝试传递与Iterator不同的提取方法&gt;你会看到编译时错误
答案 2 :(得分:0)
Java Generics只是一个编译时功能,而不是运行时功能。因此ClassCastException
答案 3 :(得分:0)
你正在插入Integers(它是Object的子类),同时检索它你有String,因此它会在类caste异常中结束
subList.add(1);
subList.add(2);
以上行将Integer添加到列表中
Map<String, Integer> map = extractor.extract(list.iterator());
它被转换为String。由于方法提取具有超类,所以它会插入很好。但是当你把它赶回去时,它会抛出阶级种姓例外。
答案 4 :(得分:0)
这个保证只是由静态类型检查给出的......因为你告诉编译器要相信你的元素属于给定类型(转换为通用类型K和V),然后在运行时忘记了这些信息(类型擦除),您实际上是插入任何对象。
如果您希望之前提出该错误,可以执行以下操作:
private static class MapResultExtractor<K, V> {
Class<K> keyClass;
Class<V> valueClass;
public MapResultExtractor(Class<K> keyClass, Class<V> valueClass) {
this.keyClass = keyClass;
this.valueClass = valueClass;
}
public Map<K, V> extract(Iterator<List<Object>> iter)
throws IOException {
Map<K, V> map = new HashMap<K, V>();
while (iter.hasNext()) {
List<Object> tuple = iter.next();
if (!keyClass.instanceOf(tuple.get(0))) throw new ClassCastException();
if (!valueClass.instanceOf(tuple.get(1))) throw new ClassCastException();
K key = (K) (tuple.get(0) == null ? null : tuple.get(0));
V value = (V) (tuple.get(1) == null ? null : tuple.get(1));
map.put(key, value);
}
return map;
}
}