黑马程序员技术交流社区

标题: 关于java数组和泛型的一点不解,求解释 [打印本页]

作者: 冯越    时间: 2012-5-21 23:47
标题: 关于java数组和泛型的一点不解,求解释
今天解题的时候一直有一个错误编译器过不去。后来才有黑马上朋友告诉我是数组和泛型的问题。那就是java1.5不允许创建泛型数组。虽然我知道了错误的所在,但我不了解错误的原因。为什么java语法要做这样的规定呢?我上sun的官网找了下资料,倒是找到了关于这个问题的描述,但鉴于英文我看的有点是懂非懂。下面是它举的两个对比的例子,希望各位黑马的朋友能帮我看看他们到底有什么不同?还有为什么java不支持带泛型的数组呢?
java代码:
// Not really allowed.
List<String>[] lsa = new List<String>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// Unsound, but passes run time store check
oa[1] = li;

// Run-time error: ClassCastException.
String s = lsa[1].get(0);

java代码:
// OK, array of unbounded wildcard type.
List<?>[] lsa = new List<?>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// Correct.
oa[1] = li;
// Run time error, but cast is explicit.
String s = (String) lsa[1].get(0);
作者: 冯越    时间: 2012-5-22 11:34
{:soso_e109:}   就木有人给解释一下吗???
作者: 云惟桉    时间: 2012-5-22 11:39
本帖最后由 云惟桉 于 2012-5-22 16:29 编辑

我想楼主引用的英文例子所说的大概意思是:如果泛型数组可行,会造成什么问题,因此禁止使用泛型数组。
如果泛型数组可行,就会有这样的情况:

List<String>[] lsa = new List<String>[10];
当类型擦出后,isa类型是List[],可以转换成Object[]:
Object o = lsa;
Object[] oa = (Object[]) o;

而数组能够记住自己的元素类型,当使用转换后的数组存入一个错误类型的元素时:
oa[0] ="Hello"会抛出异常:ArrayStoreException

但是对于泛型而言:
oa[0] = li ;   //相当于 oa[0] = new ArrayList<Integer>();
这虽然存入了一个错误类型的元素,但是泛型擦除后,相当于 oa[0] = new ArrayList();
这可以通过数组存储的检测,但使用的时候仍然会报错。

所以为了避免这种隐性的错误,就把泛型数组列入了黑名单,禁止使用了。
第二段代码,使用了通配符,我试了一试,可以通过编译并运行:
  1. public static void main(String[] args) {
  2.                
  3.                 //使用通配符的泛型数组是ok的
  4.                 List<?>[] lsa = new List<?>[10];
  5.                 Object o = lsa;
  6.                 Object[] oa = (Object[]) o;
  7.                 List<Integer> li = new ArrayList<Integer>();
  8.                 li.add(new Integer(3));
  9.                 // Correct.
  10.                 oa[1] = li;
  11.                 System.out.println(oa[1]);
  12.                
  13.                 //这边的显示转换明显是不行的,但是不明白在这里举这个例子是为什么?
  14.                 String s = (String)lsa[1].get(0);
  15.                 System.out.println(s);

  16.         }
复制代码
其实也能理解,就是?代表不确定的参数化类型,所以不指定,因此
List<Integer> li = new ArrayList<Integer>();
oa[1] = li;
不会出现错误,使用起来也合理。

但是一般不把通配符做直接的泛型定义。
原因是:假设你自定义的泛型类使用通配符?
Person<?> p = new Person<?>();

那么Person里的成员方法就变成这样:
? getPeroson()
则返回值类型是无限定的,只能赋给一个Object变量。

关于详细的泛型内容,楼主可以看张老师的视频,或参考其他的书籍。
这里说的可能比较浅,而且表达不一定正确。
但希望能帮到你啊。
作者: 刘聪    时间: 2012-5-22 12:15
冯越 发表于 2012-5-22 11:34
就木有人给解释一下吗???
  1.                 //这边的显示转换明显是不行的,但是不明白在这里举这个例子是为什么?
  2.                 String s = (String)lsa[1].get(0);
  3.                 System.out.println(s);
复制代码
其实了解java引入泛型的原因是个比较好的idea——为了防止数据类型不匹配,即希望用的时候不用在检测是不是自己想要的类型,而在泛型出现以前必须先检测此类型是否正确。为什么泛型数组不行?楼上也说了,数组能够记着自己的元素类型——因为数组前面的声明本身就是一种“伪泛型”,但这种泛型的强制检测功力不够强,而使得可以通过将他先转换为object类——Boss——然后绕过它,间接地改变其存储类型,从而不能确定它到底能装什么不能装什么,而这样也就失去了泛型的意义,同时java也会对此报错,因为你“不务正业”——既没有按照数组的方式限定类型,也没有按照泛型的方式限制类型,所以,出错了,亲~
作者: 王杰    时间: 2012-5-22 12:25

java中是不支持建立参数化类型数组的。
虽然在逻辑上来看应该是可以建立的。但是,我们得注意一件事:java的泛型和C++的泛型不一样,就像张老师在视频里说的,java的泛型是伪泛型,java的泛型只存在于编译时期!
然后我们在说说数组,数组和集合很相似,但是数组是建立以后就不可以改变的,数组中允许存入同一种类型的对象。虚拟机在将对象存入数组的时候会进行运行是检查,以确保存入的对象和运行时的类型相兼容。
String[] str = new String[10];
Object[] objs = str;//不会报错。数组的一个属性;不明白去网上查查;
objs[0]=12;//AUTOBOXING;
Sring s = str[0];//运行时期报错。类型转换异常

如果在泛型中使用数组,这样的检查就显得不那么足够了,因为泛型只在编译时期有效,在运行时期java虚拟机读取不到原来的泛型信息,就因为这个原因就造成了一个类型安全的问题,所以java设计人员避免用户创建参数化的数组。
如果我们假设泛型化的数组可以创建:
ArrayList<String>[] al = new ArrayList<String>[12];
ArrayList<Integer> in  = new ArrayList<Integer>();
in.add(12);
Object[] objs = al;//数组特性;
objs[0]= in;//不会报错哦
String ss = al[0].get(0);//类型转换异常。
这就是一个严重的类型安全问题,如果我们没有取出元素,而仅仅是存入,那么我们定义存入的是String但是却实际出入Integer;
作者: 冯越    时间: 2012-5-22 18:01
云惟桉 发表于 2012-5-22 11:39
我想楼主引用的英文例子所说的大概意思是:如果泛型数组可行,会造成什么问题,因此禁止使用泛型数组。
如 ...

thank you 啦




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2