在Java中转换为通用类型不会引发ClassCastException? [英] Casting to generic type in Java doesn't raise ClassCastException?

查看:121
本文介绍了在Java中转换为通用类型不会引发ClassCastException?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个奇怪的Java行为,似乎是一个bug。是吗?将对象转换为通用类型(例如 K )不会抛出 ClassCastException ,即使对象不是 K 的实例。这里是一个例子:

I have come across a strange behavior of Java that seems like a bug. Is it? Casting an Object to a generic type (say, K) does not throw a ClassCastException even if the object is not an instance of K. Here is an example:

import java.util.*;
public final class Test {
  private static<K,V> void addToMap(Map<K,V> map, Object ... vals) {
    for(int i = 0; i < vals.length; i += 2)
      map.put((K)vals[i], (V)vals[i+1]); //Never throws ClassCastException!
  }
  public static void main(String[] args) {
    Map<String,Integer> m = new HashMap<String,Integer>();
    addToMap(m, "hello", "world"); //No exception
    System.out.println(m.get("hello")); //Prints "world", which is NOT an Integer!!
  }
}






更新:感谢cletus和Andrzej Doyle为您提供有用的答案。由于我只能接受一个,因此我接受 Andrzej Doyle的回答,因为它导致我一个解决方案,我认为不是坏。


Update: Thanks to cletus and Andrzej Doyle for your helpful answers. Since I can only accept one, I'm accepting Andrzej Doyle's answer because it led me to a solution that I think isn't too bad. I think it's a little better way of initializing a small Map in a one-liner.

  /**
   * Creates a map with given keys/values.
   * 
   * @param keysVals Must be a list of alternating key, value, key, value, etc.
   * @throws ClassCastException if provided keys/values are not the proper class.
   * @throws IllegalArgumentException if keysVals has odd length (more keys than values).
   */
  public static<K,V> Map<K,V> build(Class<K> keyClass, Class<V> valClass, Object ... keysVals)
  {
    if(keysVals.length % 2 != 0)
      throw new IllegalArgumentException("Number of keys is greater than number of values.");

    Map<K,V> map = new HashMap<K,V>();
    for(int i = 0; i < keysVals.length; i += 2)
      map.put(keyClass.cast(keysVals[i]), valClass.cast(keysVals[i+1]));

    return map;
  }

然后你这样调用:

Map<String,Number> m = MapBuilder.build(String.class, Number.class, "L", 11, "W", 17, "H", 0.001);


推荐答案

如cletus所说,擦除意味着你不能在运行时检查它(并且由于你的转换,你不能在编译时检查这个)。

As cletus says, erasure means that you can't check for this at runtime (and thanks to your casting you can't check this at compile time).

请记住,泛型是一个编译时功能。集合对象没有任何通用参数,只有您为该对象创建的引用。这就是为什么你得到很多关于unchecked cast的警告,如果你需要downcast一个集合从原始类型或甚至 Object - 因为没有办法编译器以验证对象是否为正确的泛型类型(因为对象本身没有通用类型)。

Bear in mind that generics are a compile-time only feature. A collection object does not have any generic parameters, only the references you create to that object. This is why you get a lot of warning about "unchecked cast" if you ever need to downcast a collection from a raw type or even Object - because there's no way for the compiler to verify that the object is of the correct generic type (as the object itself has no generic type).

此外,请记住,铸造意味着什么 - 这是一种方式告诉编译器我知道你不一定检查类型匹配,但相信我,我知道他们做。当你重写类型检查(不正确),然后结束了类型不匹配,谁会怪? ; - )

Also, bear in mind what casting means - it's a way of telling the compiler "I know that you can't necessarily check that the types match, but trust me, I know they do". When you override type checking (incorrectly) and then end up with a type mismatch, who ya gonna blame? ;-)

似乎你的问题在于缺乏异构的通用数据结构。我建议你的方法的类型签名应该更像 private static< K,V& void addToMap(Map< K,V> map,List< Pair< K,V>> vals),但我不相信,对象列表基本上是一个映射,因此为了调用该方法,建立类型安全的 vals 参数将与直接填充映射的工作一样多。

It seems like your problem lies around the lack of heterogenous generic data structures. I would suggest that the type signature of your method should be more like private static<K,V> void addToMap(Map<K,V> map, List<Pair<K, V>> vals), but I'm not convinced that gets you anything really. A list of pairs basically is a map, so contructing the typesafe vals parameter in order to call the method would be as much work as just populating the map directly.

如果你真的,真的想保持你的类粗略,但添加运行时类型安全,也许下面将给你一些想法:

If you really, really want to keep your class roughly as it is but add runtime type-safety, perhaps the following will give you some ideas:

private static<K,V> void addToMap(Map<K,V> map, Object ... vals, Class<K> keyClass, Class<V> valueClass) {
  for(int i = 0; i < vals.length; i += 2) {
    if (!keyClass.isAssignableFrom(vals[i])) {
      throw new ClassCastException("wrong key type: " + vals[i].getClass());
    }
    if (!valueClass.isAssignableFrom(vals[i+1])) {
      throw new ClassCastException("wrong value type: " + vals[i+1].getClass());
    }

    map.put((K)vals[i], (V)vals[i+1]); //Never throws ClassCastException!
  }
}

这篇关于在Java中转换为通用类型不会引发ClassCastException?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆