java 不支持泛型_分析一下为什么JAVA不支持泛型类型的数组

首先大家参考一下这篇文章

sean的这篇文章大部分是对的,但是到最后的结论部分“想想看,我们本来定义的是装Map的数组,结果我们却可以往里面放任何Map,接下来如果有代码试图按原有的定义去取值,后果是什么不言自明。”,我觉得可以讨论讨论。

其实,sean的文中也提到,Java对泛型的支持其实就是在编译器中做了做手脚,增加了一些强制类型转换的代码,也就是说原来需要我们手动写的一些强制类型转换的代码,在泛型的世界里,Java编译器就帮我们做了。

下面来一步步的分析泛型数组的问题:

Java中的泛型做了什么

首先看一下Java中的泛型做了什么。看下面这段代码:

public class GenTest{

T value;publicT getValue() {returnvalue;

}public voidsetValue(T t) {

value=t;

}

}

使用javap命令反编译生成的GenTest类的class文件,可以得到下面的输出:

javap -c -p GenTest

Compiled from"GenTest.java"

public class GenTest extendsjava.lang.Object{

java.lang.Object value;publicGenTest();

Code:0: aload_01: invokespecial #12; //Method java/lang/Object."":()V

4: return

publicjava.lang.Object getValue();

Code:0: aload_01: getfield #23; //Field value:Ljava/lang/Object;

4: areturnpublic voidsetValue(java.lang.Object);

Code:0: aload_01: aload_12: putfield #23; //Field value:Ljava/lang/Object;

5: return}

我们清楚的看到,泛型T在GenTest类中就是Object类型(java.lang.Object value;)。同样,get方法和set方法也都是将泛型T当作Object来处理的。如果我们规定泛型是Numeric类或者其子类,那么在这里泛型T就是被当作Numeric类来处理的。

好,既然GenTest类中没有什么乾坤,那么我们继续看使用GenTest的时候又什么新东西:

public classUseGenTest {public static voidmain(String[] args) {

String value= "value";

GenTest test = new GenTest();

test.setValue(value);

String nv=test.getValue();

}

}

使用javap命令反编译生成的GenTest类的class文件,可以得到下面的输出:

D:\mymise\eclipse\workspace\Test\bin>javap -c -p UseGenTest

Compiled from"UseGenTest.java"

public class UseGenTest extendsjava.lang.Object{publicUseGenTest();

Code:0: aload_01: invokespecial #8; //Method java/lang/Object."":()V

4: return

public static voidmain(java.lang.String[]);

Code:0: ldc #16; //String value

2: astore_13: new #18; //class GenTest

6: dup7: invokespecial #20; //Method GenTest."":()V

10: astore_211: aload_212: aload_113: invokevirtual #21; //Method GenTest.setValue:(Ljava/lang/Object;)V

16: aload_217: invokevirtual #25; //Method GenTest.getValue:()Ljava/lang/Object;

20: checkcast #29; //class java/lang/String

23: astore_324: return}

重点在17、20和23三处。17就是调用getValue方法。而20则是关键——类型检查。也就是说,在调用getValue方法之后,并没有直接把返回值赋值给nv,而是先检查了返回值是否是String类型,换句话说,“String nv=test.getValue();”被编译器变成了“String nv=(String)test.getValue();”。最后,如果检查无误,在23处才会赋值。也就是说,如果没有完成类型检查,则会报出类似ClassCastException,而代码将不会继续向下执行,这就有效的避免了错误的出现。

也就是说:在类的内部,泛型类型就是被基类型代替的(默认是Object类型),而对外,所有返回值类型为泛型类型的方法,在真正使用返回值之前,都是会经过类型转换的。

为什么不支持泛型的数组?

根据上面的分析可以看出来,泛型其实是挺严谨的,说白了就是在“编译的时候通过增加强制类型转换的代码,来避免用户编写出可能引发ClassCastException的代码”。这其实也算是Java引入泛型的一个目的。

但是,一个颇具讽刺意味的问题出现了:如果允许了泛型数组,那么编译器添加的强制类型转换的代码就会有可能是错误的。

看下面的例子:

//下面的代码使用了泛型的数组,是无法通过编译的

GenTest genArr[] = new GenTest[2];

Object[] test=genArr;

GenTest strBuf = new GenTest();

strBuf.setValue(newStringBuffer());

test[0] =strBuf;

GenTest ref = genArr[0]; //上面两行相当于使用数组移花接木,让Java编译器把GenTest当作了GenTest

String value = ref.getValue();//这里是重点!

上面的代码中,最后一行是重点。根据本文第一部分的介绍,“String value=ref.getValue()”会被替换成“String value=(String)ref.getValue()”。当然我们知道,ref实际上是指向一个存储着StringBuffer对象的GenTest对象。所以,编译器生成出来的代码是隐含着错误的,在运的时候就会抛出ClassCastException。

但是,如果没有“String value=ref.getValue();”这行代码,那么程序可以说没有任何错误。这全都是Java中多态的功劳。我们来分析一下,对于上面代码中创建出来的GenTest对象,其实无论value引用实际指向的是什么对象,对于类中的代码来说都是没有任何影响的——因为在GenTest类中,这个对象仅仅会被当作是基类型的对象(在这里也就是Object的对象)来使用。所以,无论是String的对象,还是StringBuffer的对象,都不可能引发任何问题。举例来说,如果调用valued的hashcode方法,那么,如果value指向的是String的对象,实际执行的就是String类中的hashcode方法,如果是StringBuffer的对象,那么实际执行的就是StringBuffer类中的hashcode方法。

从这里可以看出,即使支持泛型数组也不会带来什么灾难性的后果,最多就是可能引发ClassCastException。而且平心而论,这个还是程序员自己的错误,实在算不得是Java编译器的错误。

但是从另一个角度看,这确实是个巨大的讽刺:泛型是为了消灭ClassCastException而出现的,但是在这个时候它自己却引发了ClassCastException。咱们中国人把这个叫做搬起石头砸自己的脚。

当然制定JSR的那帮子人可能没学过中文,但是他们肯定是发现了这个令他们纠结的问题。被标榜为Java 5重要feature的泛型竟然陷入了这么一个怪圈。于是,他们在某个月黑风高的晚上,在某个猥琐的会议室内,悄悄的决定一不做二不休——不支持泛型的数组了。(本段内容系作者猜测,并无任何事实根据,如有雷同,纯粹巧合。)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值