转自:
http://www.myexception.cn/mobile/692129.html
向作者致谢!
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
大多数这些需要注意的问题,都是由类型擦除引起的。
一、基本类型
不能用类型参数替换基本类型。就比如,没有ArrayList<double>,只有ArrayList<Double>。因为当类型擦除后,ArrayList的原始类型变为Object,但是Object类型不能存储double值,只能引用Double的值。
二、运行时类型查新
举个例子:
ArrayList<String> arrayList=new ArrayList<String>();
因为类型擦除之后,ArrayList<String>只剩下原始类型,泛型信息String不存在了。
那么,运行时进行类型查询的时候使用下面的方法是错误的
if( arrayList instanceof ArrayList<String>)
java限定了这种类型查询的方式
if( arrayList instanceof ArrayList<?>)
三、异常
1、不能抛出也不能捕获泛型类的对象。事实上,泛型类扩展Throwable都不合法。例如:下面的定义将不会通过编译:
public class Problem<T> extends Exception{......}
为什么不能扩展Throwable,因为异常都是在运行时捕获和抛出的,而在编译的时候,泛型信息全都会被擦除掉,那么,假设上面的编译可行,那么,在看下面的定义:
try{
}catch(Problem<Integer> e1){
。。
}catch(Problem<Number> e2){
...
}
类型信息被擦除后,那么两个地方的catch都变为原始类型Object,那么也就是说,这两个地方的catch变的一模一样,就相当于下面的这样
try{
}catch(Problem<Object> e1){
。。
}catch(Problem<Object> e2){
...
这个当然就是不行的。就好比,catch两个一模一样的普通异常,不能通过编译一样:
try{
}catch(Exception e1){
。。
}catch(Exception e2){//编译错误
...
2、不能再catch子句中使用泛型变量
public static <T extends Throwable> void doWork(Class<T> t){
try{
...
}catch(T e){ //编译错误
...
}
}
因为泛型信息在编译的时候已经变味原始类型,也就是说上面的T会变为原始类型Throwable,那么如果可以再catch子句中使用泛型变量,那么,下面的定义呢:
public static <T extends Throwable> void doWork(Class<T> t){
try{
...
}catch(T e){ //编译错误
...
}catch(IndexOutOfBounds e){
}
}
,根据异常捕获的原则,一定是子类在前面,父类在后面,那么上面就违背了这个原则。即使你在使用该静态方法的使用T是ArrayIndexOutofBounds,在编译之后还是会变成Throwable,ArrayIndexOutofBounds是IndexOutofBounds的子类,违背了异常捕获的原则。所以java为了避免这样的情况,禁止在catch子句中使用泛型变量。
但是在异常声明中可以使用类型变量。下面方法是合法的。
public static<T extends Throwable> void doWork(T t) throws T{
try{
...
}catch(Throwable realCause){
t.initCause(realCause);
throw t;
}
}
上面的这样使用是没问题的。
四、数组
不能声明参数化类型的数组。如:
Pair<String>[] table = newPair<String>(10); //ERROR
这是因为擦除后,table的类型变为Pair[],可以转化成一个Object[]。
Object[] objarray =table;
数组可以记住自己的元素类型,下面的赋值会抛出一个ArrayStoreException异常。
objarray ="Hello"; //ERROR
对于泛型而言,擦除降低了这个机制的效率。下面的赋值可以通过数组存储的检测,但仍然会导致类型错误。
objarray =new Pair<Employee>();
提示:如果需要收集参数化类型对象,直接使用ArrayList:ArrayList<Pair<String>>最安全且有效。
五、泛型类型的实例化
不能使用像new T(...),new T[...],T.class这样的表达式
不能实例化泛型类型。如,
first = newT(); //ERROR
是错误的,类型擦除会使这个操作做成new Object()。
可以使用Class.newInstance()方法:
cl.newInstanc();
不能建立一个泛型数组。
public<T> T[] minMax(T[] a){
T[] mm = new T[2]; //ERROR
...
}
类似的,擦除会使这个方法总是构靠一个Object[2]数组。但是,可以用反射构造泛型对象和数组。
利用反射,调用Array.newInstance:
publicstatic <T extends Comparable> T[]minmax(T[] a)
{
T[] mm == (T[])Array.newInstance(a.getClass().getComponentType(),2);
...
// 以替换掉以下代码
// Obeject[] mm = new Object[2];
// return (T[]) mm;
}
六、静态上下文
不能在静态域或者方法中引用类型变量。
public class Test<M> {
public static void test(M m) { //error
}
}
这句话的错误提示是无法在静态上下文中引用非静态类 M.
因为test方法是静态的可以用类名直接调用,而M只有在类Test创建对象的时候才会明确.
test静态方法存在的时候还没明确M是什么.
所以静态方法不可以访问类上定义的泛型.
如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上.
可以改成:
public class Test {
public static <M> void test(M m) { //right
}
}
七、类型擦除后的冲突
1、
当泛型类型被擦除后,创建条件不能产生冲突。如果在Pair类中添加下面的equals方法:
class Pair<T> {
public boolean equals(T value) {
return null;
}
}
考虑一个Pair<String>。从概念上,它有两个equals方法:
booleanequals(String); //在Pair<T>中定义
boolean equals(Object); //从object中继承
但是,这只是一种错觉。实际上,擦除后方法
booleanequals(T)
变成了方法 booleanequals(Object)
这与Object.equals方法是冲突的!当然,补救的办法是重新命名引发错误的方法。
2、
泛型规范说明提及另一个原则“要支持擦除的转换,需要强行制一个类或者类型变量不能同时成为两个接口的子类,而这两个子类是同一接品的不同参数化。”
下面的代码是非法的:
class Calendar implements Comparable<Calendar>{ ... }
class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar>{...} //ERROR
GregorianCalendar会实现Comparable<Calender>和Compable<GregorianCalendar>,这是同一个接口的不同参数化实现。
这一限制与类型擦除的关系并不很明确。非泛型版本:
class Calendar implements Comparable{ ... }
class GregorianCalendar extends Calendar implements Comparable{...} //ERROR
是合法的。
有可能与合成的桥方法产生冲突有关:
public int compareTo(Object other) {return compareTo((X) other);}
方法体产生冲突,X应是Calender还是GregorianCalendar,不确定
八、泛型中 参数化类型不考虑继承关系
ArrayList<String> arrayList1=new ArrayList<Object>();//编译错误
ArrayList<Object> arrayList1=new ArrayList<String>();//编译错误
分享到:
相关推荐
Java 范型Java 范型
Java 范型攻略篇
JAVA范型指南:讲述范型,范型通配符,范型方法,范型与久代码(无范型)的融合,范型的擦除与翻译等...
java范型[参考].pdf
java范型[参照].pdf
java1.5范型编程指南.docjava1.5范型编程指南.docjava1.5范型编程指南.docjava1.5范型编程指南.docjava1.5范型编程指南.doc
java范型学习.............
Java范型机制 Java范型机制 Java范型机制 Java范型机制Java范型机制 Java范型机制
java 泛型类的类型识别示例 java 泛型类的类型识别示例 java 泛型类的类型识别示例
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是...
Java程序设计范型和枚举PPT教案学习.pptx
Java 实现泛型List的源码,基本实现了List接口的全部所有方法。欢迎大家发表自己的观点和建议。
范型参考 (1).java
范型参考 (2).java
消息传递范型与C/S范型双范型的主数据管理机制,陈晓云,邢乔金,本文针对主数据管理(MDM)存在的问题提出了一种基于消息传递机制范型与C/S范型双范型的主数据管理机制,当各个分系统的数据有所变化�
C++多范型设计,ISBN:9787508318240,作者:(美)James O.Coplien著;鄢爱兰,周辉等译;鄢爱兰译
暂时仅仅设计了以下几个功能点: 1.点对点单人聊天; 2.多人在线同时聊天; 3.用户可以自由加入和退出系统; 4.具备用户在线状态监听;
本例子说明演示了Java范型使用的动机、范型类的使用、范型方法的使用,以及范型的缺陷:类型擦除(type erasure).因为,现在Java的反射机制不能确定集合中的对象类型! 在实际应用中,如果能够灵活应用范型和反射...
范型程序设计与 STL.pdf,大小约 300K。
分布式系统原理与范型第二版课后习题答案 分布式系统原理与范型第二版课后习题答案 分布式系统原理与范型第二版课后习题答案 分布式系统原理与范型第二版课后习题答案