- 浏览: 7325894 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (1546)
- 企业中间件 (236)
- 企业应用面临的问题 (236)
- 小布Oracle学习笔记汇总 (36)
- Spring 开发应用 (54)
- IBatis开发应用 (16)
- Oracle基础学习 (23)
- struts2.0 (41)
- JVM&ClassLoader&GC (16)
- JQuery的开发应用 (17)
- WebService的开发应用 (21)
- Java&Socket (44)
- 开源组件的应用 (254)
- 常用Javascript的开发应用 (28)
- J2EE开发技术指南 (163)
- EJB3开发应用 (11)
- GIS&Mobile&MAP (36)
- SWT-GEF-RCP (52)
- 算法&数据结构 (6)
- Apache开源组件研究 (62)
- Hibernate 学习应用 (57)
- java并发编程 (59)
- MySQL&Mongodb&MS/SQL (15)
- Oracle数据库实验室 (55)
- 搜索引擎的开发应用 (34)
- 软件工程师笔试经典 (14)
- 其他杂项 (10)
- AndroidPn& MQTT&C2DM&推技术 (29)
- ActiveMQ学习和研究 (38)
- Google技术应用开发和API分析 (11)
- flex的学习总结 (59)
- 项目中一点总结 (20)
- java疑惑 java面向对象编程 (28)
- Android 开发学习 (133)
- linux和UNIX的总结 (37)
- Titanium学习总结 (20)
- JQueryMobile学习总结 (34)
- Phonegap学习总结 (32)
- HTML5学习总结 (41)
- JeeCMS研究和理解分析 (9)
最新评论
-
lgh1992314:
[u][i][b][flash=200,200][url][i ...
看看mybatis 源代码 -
尼古拉斯.fwp:
图片根本就不出来好吧。。。。。。
Android文件图片上传的详细讲解(一)HTTP multipart/form-data 上传报文格式实现手机端上传 -
ln94223:
第一个应该用排它网关吧 怎么是并行网关, 并行网关是所有exe ...
工作流Activiti的学习总结(八)Activiti自动执行的应用 -
ZY199266:
获取不到任何消息信息,请问这是什么原因呢?
ActiveMQ 通过JMX监控Connection,Queue,Topic的信息 -
xiaoyao霄:
DestinationSourceMonitor 报错 应该导 ...
ActiveMQ 通过JMX监控Connection,Queue,Topic的信息
sean的这篇文章大部分是对的,但是到最后的结论部分“想想看,我们本来定义的是装Map<Integer, String>的数组,结果我们却可以往里面放任何Map,接下来如果有代码试图按原有的定义去取值,后果是什么不言自明。”,我觉得可以讨论讨论。
其实,sean的文中也提到,Java对泛型的支持其实就是在编译器中做了做手脚,增加了一些强制类型转换的代码,也就是说原来需要我们手动写的一些强制类型转换的代码,在泛型的世界里,Java编译器就帮我们做了。
下面来一步步的分析泛型数组的问题:
Java中的泛型做了什么
首先看一下Java中的泛型做了什么。看下面这段代码:<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->public class GenTest<T> {
T value;
public T getValue() {
return value;
}
public void setValue(T t) {
value = t;
}
}
T value;
public T getValue() {
return value;
}
public void setValue(T t) {
value = t;
}
}
使用javap命令反编译生成的GenTest类的class文件,可以得到下面的输出:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->javap -c -p GenTest
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
}
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的时候又什么新东西:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->public class UseGenTest {
public static void main(String[] args) {
String value = "value";
GenTest<String> test = new GenTest<String>();
test.setValue(value);
String nv = test.getValue();
}
}
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文件,可以得到下面的输出:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->D:\mymise\eclipse\workspace\Test\bin>javap -c -p UseGenTest
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
}
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引入泛型的一个目的。但是,一个颇具讽刺意味的问题出现了:如果允许了泛型数组,那么编译器添加的强制类型转换的代码就会有可能是错误的。
看下面的例子:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->//下面的代码使用了泛型的数组,是无法通过编译的
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();// 这里是重点!
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();”这行代码,那么程序可以说没有任何错误。这全都是Java中多态的功劳。我们来分析一下,对于上面代码中创建出来的GenTest对象,其实无论value引用实际指向的是什么对象,对于类中的代码来说都是没有任何影响的——因为在GenTest类中,这个对象仅仅会被当作是基类型的对象(在这里也就是Object的对象)来使用。所以,无论是String的对象,还是StringBuffer的对象,都不可能引发任何问题。举例来说,如果调用valued的hashcode方法,那么,如果value指向的是String的对象,实际执行的就是String类中的hashcode方法,如果是StringBuffer的对象,那么实际执行的就是StringBuffer类中的hashcode方法。
从这里可以看出,即使支持泛型数组也不会带来什么灾难性的后果,最多就是可能引发ClassCastException。而且平心而论,这个还是程序员自己的错误,实在算不得是Java编译器的错误。
但是从另一个角度看,这确实是个巨大的讽刺:泛型是为了消灭ClassCastException而出现的,但是在这个时候它自己却引发了ClassCastException。咱们中国人把这个叫做搬起石头砸自己的脚。
当然制定JSR的那帮子人可能没学过中文,但是他们肯定是发现了这个令他们纠结的问题。被标榜为Java 5重要feature的泛型竟然陷入了这么一个怪圈。于是,他们在某个月黑风高的晚上,在某个猥琐的会议室内,悄悄的决定一不做二不休——不支持泛型的数组了。(本段内容系作者猜测,并无任何事实根据,如有雷同,纯粹巧合。)
发表评论
-
【转】Coherence Event Processing by using Map Trigger Feature
2013-06-25 14:02 2077This article shows how to proc ... -
【转】Distributed Data Management in Oracle Coherence
2013-06-25 13:55 1888This article shows how to prov ... -
【转】How to distribute Spring Beans by using Coherence
2013-06-21 17:24 1962转载自: http://www.onli ... -
关于H2使用的那些事
2012-12-14 16:40 27164在项目中采用H2作为工 ... -
【转】ConcurrentHashMap之实现细节
2012-12-10 14:32 1767Conc ... -
【转】Java并发编程J.U.C之Condition
2012-12-10 13:14 1903转载自http://www.golde ... -
【转】Java并发编程J.U.C之锁的获取与释放
2012-12-10 13:07 1611转载自 http://www.g ... -
【转】JUC 基础内容概述
2012-12-10 13:01 2042Concurrent Programming in Jav ... -
【转】Java并发编程之ConcurrentHashMap
2012-12-10 12:56 1772ConcurrentHashMap Conc ... -
【转】单例模式完全解析
2012-12-07 12:58 1698本文将探讨单例模式的各种情况,并给出相应的建议。 单例模式应该 ... -
【转】JAVA并发容器代码随读
2012-12-06 15:29 2943转载自 http://rdc.taobao.c ... -
【转】Spring 事务管理高级应用难点剖析:
2012-12-04 16:29 2219转载自 http://www.ibm.com/deve ... -
【转】Struts2的线程安全 和Struts2中的设计模式----ThreadLocal模式
2012-12-04 15:11 4475转载自 http://downpour.ite ... -
Hibernate延迟加载和OpenSessionInView
2012-12-03 17:13 3165ThreadLocal& ... -
关于ThreadLocal分析和总结
2012-12-03 15:56 1856什么是ThreadLocal? ... -
【转】java线程阻塞中断和LockSupport的常见问题
2012-12-03 13:30 1788转载自 http://www.iteye ... -
java 中 ReentrantReadWriteLock的读锁和写锁的使用
2012-11-30 17:14 10434jdk文档中关于Reentr ... -
java 多线程中Semaphore信号量
2012-11-30 15:39 3748Semaphore信号量: 可以维护当前 ... -
Java 文件锁的使用
2012-11-30 13:21 4425多线程-内部锁、重进入 多线程 ... -
【转】线程安全与锁优化
2012-11-29 16:17 1880转载自http://xussen.iteye.com/blo ...
相关推荐
在实际编程中,当我们需要一个泛型类型的数组时,Java会提示我们使用`List<T>`或者其他集合框架类来替代,因为这些集合类可以容纳泛型对象。 在Java中,我们可以创建非泛型的数组,比如`String[] array = new ...
在Java编程中,"泛型自定义数组大小"是一个重要的概念,它涉及到数据结构和算法的基础,以及面向对象编程中的类型安全。泛型是Java 5引入的一个特性,旨在提高代码的类型安全性,减少类型转换的冗余,并提供编译时的...
总结起来,"实例185 - 自定义泛型化数组类"是关于如何利用Java泛型特性创建安全且灵活的数组容器的一个案例。通过理解泛型、数组的限制以及如何结合两者,我们可以编写出更强大、更安全的代码,提升代码的复用性和...
2. **类型擦除的影响**:Java 泛型在运行时会被擦除为其原始类型,这限制了泛型数组的使用。 3. **替代方案**:在大多数情况下,使用 `List<T>` 而不是泛型数组更为可行,因为它提供了更多的灵活性并且避免了类型...
Java 中泛型数组的关系确实有些复杂,不允许直接创建泛型数组,本文将分析其中的原因,并总结一些创建泛型数组的方式。 首先,让我们看看数组和泛型的关系。数组相比于 Java 类库中的容器类是比较特殊的,主要体现...
本文详细介绍了Java中使用泛型反转数组的方法。通过泛型,我们可以编写类型安全的代码,同时避免类型转换的需要。泛型反转方法可以处理任何类型的数组...尽管Java泛型有其限制,但它们仍然是Java编程中一个强大的工具。
Java泛型是Java编程语言中一个强大的特性,它允许在定义类、接口和方法时使用类型参数,从而实现参数化类型。泛型的主要目标是提高代码的类型安全性和重用性,减少类型转换的麻烦,并在编译时捕获可能的类型错误。...
- 由于历史原因,Java不支持泛型数组的直接创建,如`new MyList[5]`是非法的。 - 可以通过类型安全的工厂方法或运行时转型解决这个问题。 通过以上知识点,我们可以看到Java泛型在编程中的重要性和灵活性。理解和...
总之,虽然Java泛型不直接支持创建泛型数组,但通过反射API,我们可以动态地创建指定类型的数组。这种方法在处理不确定类型的场景下非常有用,但需要注意,反射API的使用应当谨慎,因为它可能会降低代码的性能,且...
Java 封装数组之改进为泛型数组操作是 Java 语言中的一种重要技术,旨在将基本类型数组封装为泛型数组,以提供多种类型数组的操作。下面将对 Java 封装数组之改进为泛型数组操作进行详细讲解。 一、泛型数组相关...
Java 生成随机字符串数组的实例详解是一种常见的编程任务,主要是利用Collections.sort()方法对泛型为String的List进行排序。下面是一个详细的实例详解,介绍了生成随机字符串数组的步骤和相关知识点。 知识点1:...
1. **类型擦除**: Java泛型在编译后会进行类型擦除,也就是说,所有的泛型类在运行时都会退化为未使用泛型的原始形式。这意味着在运行时无法检查泛型类型,但编译时的类型检查可以避免很多错误。 2. **边界通配符**...
感谢所有为Java泛型做出贡献的人们,包括设计者、实现者以及提供反馈和支持的社区成员。泛型是Java语言的一个重要特性,极大地提高了代码的质量和可维护性。 以上就是基于给定文件信息对Java 1.5泛型指南的主要知识...
7. **泛型数组**:Java不支持直接创建泛型数组,因为类型擦除会导致编译器无法检查数组元素类型。但是,可以通过类型转换间接创建,如`List[] stringLists = (List[]) new List[10];`,但这种操作存在潜在风险。 8....
由于Java不支持直接创建泛型数组,所以声明数组为Object类型的,然后转型即可。 构造方法 ArrayStack类提供了一个无参构造方法,该方法创建一个空的泛型栈,大小为SIZE(默认为10)。在构造方法中,初始化了数组和...
- **泛型数组**:Java 5引入了泛型,允许创建泛型数组以增强类型安全性。 - **数组操作函数**:Java的`Arrays`类提供了许多实用方法,如`sort()`排序数组,`equals()`比较数组内容,`copyOf()`复制数组等。 - **...
这样做会导致类型安全问题,因为在运行时可以将不兼容的泛型类型数组赋值给其他类型的数组。不过,可以使用未绑定的通配符如new List[3]来创建数组,但这只是一种妥协的解决方案,因为数组元素的实际类型在编译时是...