- 浏览: 1325499 次
- 性别:
- 来自: 湖南澧縣
文章分类
最新评论
-
虾米小尹:
不行啊!2.2-0.25=1.9500000000000002 ...
JavaScript浮点数运算 —— 精度问题 -
heluping000000:
引用String a= "abc",首先在 ...
String,到底创建了多少个对象? -
mack:
谢谢分享matcher.appendReplacement(s ...
string.replaceAll()中的特殊字符($ \)与matcher.appendReplacement -
wzt3309:
完全理解,比网上其他资料都要详细
String,到底创建了多少个对象? -
u014771876:
Java中十六进制转换 Integer.toHexString()
Java中的泛型做了什么
首先看一下Java中的泛型做了什么。看下面这段代码:
T value;
public T getValue() {
return value;
}
public void setValue(T t) {
value = t;
}
}
使用javap命令反编译生成的GenTest类的class文件,可以得到下面的输出:
Compiled from " GenTest.java "
public class GenTest extends java.lang.Object{
java.lang.Object value;
public GenTest();
Code:
0 : aload_0
1 : invokespecial # 12 ; // Method java/lang/Object."<init>":()V
4 : return
public java.lang.Object getValue();
Code:
0 : aload_0
1 : getfield # 23 ; // Field value:Ljava/lang/Object;
4 : areturn
public void setValue(java.lang.Object);
Code:
0 : aload_0
1 : aload_1
2 : 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 static void main(String[] args) {
String value = " value " ;
GenTest < String > test = new GenTest < String > ();
test.setValue(value);
String nv = test.getValue();
}
}
使用javap命令反编译生成的GenTest类的class文件,可以得到下面的输出:
Compiled from " UseGenTest.java "
public class UseGenTest extends java.lang.Object{
public UseGenTest();
Code:
0 : aload_0
1 : invokespecial # 8 ; // Method java/lang/Object."<init>":()V
4 : return
public static void main(java.lang.String[]);
Code:
0 : ldc # 16 ; // String value
2 : astore_1
3 : new # 18 ; // class GenTest
6 : dup
7 : invokespecial # 20 ; // Method GenTest."<init>":()V
10 : astore_2
11 : aload_2
12 : aload_1
13 : invokevirtual # 21 ; // Method GenTest.setValue:(Ljava/lang/Object;)V
16 : aload_2
17 : invokevirtual # 25 ; // Method GenTest.getValue:()Ljava/lang/Object;
20 : checkcast # 29 ; // class java/lang/String
23 : astore_3
24 : 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 < String > genArr[] = new GenTest < String > [ 2 ]; // 此句无法通过编译 ,不能创建泛型数组
Object[] test = genArr;
GenTest < StringBuffer > strBuf = new GenTest < StringBuffer > ();
strBuf.setValue( new StringBuffer());
test[ 0 ] = strBuf ;
GenTest < String > ref = genArr [ 0 ]; // 上面两行相当于使用数组移花接木,让Java编译器把GenTest<StringBuffer>当作了GenTest<String>
String value = ref.getValue(); // 这里是重点!
上面的代码中,最后一行是重点。根据本文第一部分的介绍,“
String value
=
ref.getValue()
”会被替换成“
String value
=
(String)ref.getValue()
”。当然我们知道,ref实际上是指向一个存储着StringBuffer对象的GenTest对象。所以,编译器生成出来的代码是隐含着错误的,在运的时候就会抛出ClassCastException。
但是,如果没有“
String value
=
ref.getValue();
”这行代码,那么程序可以说没有任何错误
(注,原文章第一行代码还是有点问题的,除非使用Array.newInstance(
GenTest
.class,2)来动态的创建)。这全都是Java中多态的功劳。我们来分析一下,对于上面代码中创建出来的
GenTest
对象,其实无论value引用实际指向的是什么对象,对于类中的代码来说都是没有任何影响的——因为在GenTest类中,这个对象仅仅会被当作是基类型的对象(在这里也就是Object的对象)来使用。所以,无论是String的对象,还是StringBuffer的对象,都不可能引发任何问题。举例来说,如果调用valued的hashcode方法,那么,
如果value指向的是String的对象,实际执行的就是String类中的hashcode方法,如果是StringBuffer的对象,那么实际执行的就是StringBuffer类中的hashcode方法。
从这里可以看出,即使支持泛型数组也不会带来什么灾难性的后果,最多就是可能引发ClassCastException。而且平心而论,这个还是程序员自己的错误,实在算不得是Java编译器的错误。
但是从另一个角度看,这确实是个巨大的讽刺:泛型是为了消灭ClassCastException而出现的,但是在这个时候它自己却引发了ClassCastException。咱们中国人把这个叫做搬起石头砸自己的脚。
当然制定JSR的那帮子人可能没学过中文,但是他们肯定是发现了这个令他们纠结的问题。被标榜为Java
5重要feature的泛型竟然陷入了这么一个怪圈。于是,他们在某个月黑风高的晚上,在某个猥琐的会议室内,悄悄的决定一不做二不休——不支持泛型的数组了。(本段内容系作者猜测,并无任何事实根据,如有雷同,纯粹巧合。)
http://www.blogjava.net/deepnighttwo/articles/298426.html
评论
public class GenericTest { public static void main(String[] args) throws Exception { Bean<String> bean = new Bean<String>(); Method method = bean.getClass().getMethod("setValue", Object.class); method.invoke(bean, 5); System.out.println(bean.getValue()); } public static class Bean<T> { T value = null; public T getValue() { return value; } public void setValue(T value) { this.value = value; } } }
发表评论
-
Java正则表达式
2014-03-14 10:16 1713Java正则表达式详解 作者:jzj 文 ... -
类的初始化与清理
2013-06-24 22:20 1407初始化时内存清零 当创建一个对象时,首先将在堆上为这个对象分 ... -
protected,这个错了吗?
2013-06-24 22:17 1196这几天对protected修饰符有点迷糊,随便找同事要了一本 ... -
Java中BigDecimal的8种舍入模式
2013-06-21 18:42 2142java.math.BigDecimal不可变的、任意精度的 ... -
Tomcat性能参数设置
2010-12-27 15:35 34691默认参数不适合生产环境使用,因此需要修改一些参数 1、 ... -
Java 6 JVM参数选项大全
2010-12-14 11:16 1596http://kenwublog.com/docs/java6 ... -
对象的安全构造
2013-06-21 18:43 1504在构造期间,不要公布“this”引用 一种可以将数据争用引 ... -
Java断言(assert)—— 转
2010-06-20 10:36 12032一、概述 在C和C++语言中都有assert关键,表示断言。 ... -
eclipse调试
2010-06-04 00:11 8000eclipse远程调试 在eclipse3.4前,远程调试时 ... -
protected,你真的理解了吗?
2010-05-09 17:56 2081Java中的访问控制修饰符有四个级别,但属protected最 ... -
利用反射进行深层克隆
2010-05-05 21:02 3626最近在看《effective java ... -
类与类之间的几种关系
2010-05-03 13:49 2374类和类、类和接口、接 ... -
运行java
2010-05-03 13:47 1008用javac命令编译一个打包的类时,如果没有加参数" ... -
Java内存模型与volatile
2010-04-25 13:21 18498内存模型描述的是程序 ... -
中断线程
2010-04-24 21:19 8911中断线程 线程的thread.i ... -
java中的关键字、保留字、标示符
2010-04-07 23:48 3327关键字 Java的关键字对java的编译器有特殊的意义, ... -
Java中的浮点数剖析
2010-04-07 23:27 4652定点数表达法的缺点在于其形式过于僵硬,固定的小数点位置决定了固 ... -
线程间的同步与互斥
2010-03-23 21:29 2261线程间的同步(实指线程间的通信):一般来说,一个线程相对于另 ... -
UTF-16、UTF-16BE、UTF-16LE编码方式的区别
2010-03-23 21:20 9693import java.io.IOException; ... -
final、finally、finalize
2010-01-22 01:15 2330final关键字 先看看final关键字,它可以被用于以下几个 ...
相关推荐
主要介绍了java 用泛型参数类型构造数组详解及实例的相关资料,需要的朋友可以参考下
(调用的是泛型方法)泛型与数组(只有可具体化类型可以创建数组):数组是协变类型 String数组是Object数组的字类型 但是对于list而言,则不是什么是方
java 泛型详解 实例 class Point class Notepad,V>{ // 此处指定了...通配符、受限泛型、泛型无法向上转型、泛型接口、泛型方法、通过泛型方法返回泛型类型实例、使用泛型统一传入的参数类型、泛型数组、泛型的嵌套设置
介绍了Java编程思想里的泛型实现一个堆栈类,有需要的朋友可以参考一下
本书共22章,包括操作符、控制执行流程、访问权限控制、复用类、多态、接口、通过异常处理错误、字符串、泛型、数组、容器深入研究、JavaI/O系统、枚举类型、并发以及图形化用户界面等内容。这些丰富的内容,包含了...
向数组中添加3个int类型的元素并能获取,这没问题。 但是如果我们的场景不再需要int类型的元素,而是需要String类型的,那怎么办? 很显然,继续使用该类会报错,报错的原因很简单:我们向数组中添加的元素是String...
目录一、泛型概述二、泛型定义(1)泛型类(2)泛型方法2.1 泛型可变参数(3)泛型接口(4)类型限定三、泛型使用(1)类型通配符1.1 上限1.2 下限四、泛型擦除五、泛型数组 一、泛型概述 泛型,即是参数化类型。在...
本书共22章,包括操作符、控制执行流程、访问权限控制、复用类、多态、接口、通过异常处理错误、字符串、泛型、数组、容器深入研究、JavaI/O系统、枚举类型、并发以及图形化用户界面等内容。这些丰富的内容,包含了...
推荐优质Java课程 疯狂Java语言编程 Java入门到进阶教程 01.Java语言概述(共21页).ppt 推荐优质Java课程 疯狂Java语言编程 Java入门到进阶教程 02.理解结构化程序设计_理解面向对象(共25页).ppt 推荐优质Java...
推荐优质Java课程 疯狂Java语言编程 Java入门到进阶教程 01.Java语言概述(共21页).ppt 推荐优质Java课程 疯狂Java语言编程 Java入门到进阶教程 02.理解结构化程序设计_理解面向对象(共25页).ppt 推荐优质Java...
本书共22章,包括操作符、控制执行流程、访问权限控制、复用类、多态、接口、通过异常处理错误、字符串、泛型、数组、容器深入研究、Java I/O系统、枚举类型、并发以及图形化用户界面等内容。这些丰富的内容,包含了...
本书共22章,包括操作符、控制执行流程、访问权限控制、复用类、多态、接口、通过异常处理错误、字符串、泛型、数组、容器深入研究、Java I/O系统、枚举类型、并发以及图形化用户界面等内容。这些丰富的内容,包含了...
但其实Java泛型还是有挺多tricky的东西的,编译器在背后为我们做了很多事。下面我们来看看有关Java泛型容易忽视的点。 泛型不支持协变 什么是协变?举个例子。 class Fruit{} class Apple extends Fruit...
assignTwoDime.java 为二维数组赋值 getMaxElem.java 获取数组中的最大元素 incCapicity.java 演示StingBuffer的容量增长 SortDemo.java 排序示例 travelTwoDime.java 遍历二维数组 traversing.java 遍历一维...
一 为什么我们需要泛型? 二 泛型类、泛型接口、泛型方法、泛型通配符 2.1 泛型类: 2.2 泛型接口: 2.3 泛型方法: 2.4 泛型通配符: 三 限定类型变量 四 泛型中的约束和局限性 4.1 不能用基本类型实例化类型参数 ...
java泛型源码Java泛型用法 步骤1 原始类型有问题。 第2步 使用泛型类型。 第三步 车库和车辆。 原始类型。 第四步 首先尝试生成车库。 木星在我的车库里。 第5步 泛型上限。 第6步 TripleGarage 步骤7 试图使用泛型...
本书共22章,包括操作符、控制执行流程、访问权限控制、复用类、多态、接口、通过异常处理错误、字符串、泛型、数组、容器深入研究、Java I/O系统、枚举类型、并发以及图形化用户界面等内容。这些丰富的内容,包含了...
使用Java泛型转换C#ref参数 当代码中定义事件委托时转换C#事件,或者是System.Action或System.Func代理之一 将所有类型的数组从C#转换成Java 对于继承和接口的所有方面,从C#到Java的无瑕疵转换 允许自定义替换...
5 、解释一下Java中的泛型是什么,以及如何使用泛型? 6 、解释一下Java中的多线程是什么,以及如何创建和管理线程? 7 、解释一下Java中的反射机制是什么,以及如何使用反射? 8 、解释一下Java中的注解是什么,...
本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...