欢迎关注公众号:Java编程中心。回复【学习资料】,免费获取免费学习视频等…欢迎热爱Java的朋友加入,共同进步。
1.Java语言泛型的实现
Java语言实现泛型时,为了更好保证向后兼容以及更安全的类型检查,使用了与c++不同的语法:擦拭。所谓的擦拭时是指编译时清楚程序中的所有泛型信息,并将其转化为等价的非泛型代码。这样,程序编译的就是原始的字节码文件,保证了向后兼容。
简单的说,擦拭的最基本的规则如下:
•擦除尖括号的所有类型参数类型信息
•将所有与参数类型都替换为Object,如果该类型参数带有限制参数条件,就替换为限制条件中地第一个限定类型。
•擦除类型参数信息后,在所有类型不正确的地方,插入适当的强制类型转换。通常将泛型类型经擦拭之后生成的类型称为相应泛型类型的原始类型。例如,如下泛型接口:
public interface Collection<E> extends Iterable<E>{
boolean add(E e);
...........
}
编译时,将被擦拭为类似于如下代码的原始类型:
public interface Collection extends Iterable{
boolean add(Object o);
...........
}
而下述使用的泛型类型的代码:
Lis ls=new ArrayList();
ls.add("first");
ls.add("second");
String s=(String)ls.get(0);
使用擦拭法,Java语言在不被破坏现有类库的情况下实现了泛型,并保证了向后兼容性。擦拭使得现有非泛型代码能够在不变的状态下继续使用。正如前面所见,下面代码在Java SE5之后使用:
Comparable c=new Integer("1);
上述语句中,使用泛型接口Compparable时,并没有指定类型实际参数,即使用泛型类型时,可以直接使用其原始类型。因此,下列语句都是正确的:
GenericList<Integer> list1=new GenericList();
GenericList list2=new GenericList<Integer>();
GenericList list3=new GenericList();
Java语言泛型的局限性
Java语言的泛型使用擦拭法,程序中的类型参数被途欢为object或类型的限定类型,所以,它不像C++语言一样真正的代表任何类型,而只能代表Object或类型参数的限定类型。因此,Java语言的泛型不像大多数语言一样功能强大。例如:
public class ErrorDefGenerics{
public static void main(String[]args){
Dog d=new Dog();
Cat c=new Cat();
speak(d);
speak(c);
static <T> void speak(T speaker){
speaker.talk();
}
}
class Dog{
void talk(){}
}
class Cat{
vodi talk(){}
}
具有C++等语言编程经验的程序员一定会认为上述代码是合法的。不过,事实上,它在Java语言中就不能通过编译。编译器会报告方法speak的参数speaker中没有成员方法talk,这是因为该参数的类型擦拭为Object,而类Object中没有成员方法talk。如果希望使用Java语言的泛型完成上述代码所描述的工作,就应该定义一个包含了方法talk的接口。例如,上述代码改成下面的的样子就是合法的:
public class ErrorDefGenerics{
public static void main(String[]args){
Dog d=new Dog();
Cat c=new Cat();
speak(d);
speak(c);
}
static <T> void speak(T speaker){
speaker.talk();
}
}
interface Speaks{
void talk();
}
class Dog implements Speaks{
public void talk(){}
}
class Cat implements Speaks{
public vodi talk(){}
}
不过读者可能就已经发现,上述代码在,使用泛型没有任何的好处,甚至会使程序的可读性变差。方法speak完全可以改为下述非泛型形式:
static void speak(Speaks speaker){
speaker.talk();
}
再看一个例子
tatic <T> T add(T a,T b){
return a+b;
}
java语言中上述代码是不合法的,以为两个Object对象不能相加。上述两个例子演示了java语言泛型的局限性。事实上,Java语言泛型的局限性还表现在其他 的许多方面。。例如:
GenericList ints=new GenericList();
System.out.print(ints instanceof GenericList);//编译错误,运行时参数类型信息已经被擦除
GenericList []arr=new GenericList[10];
编译时,有关泛型的所有具体类型参数信息被擦除,运行时,系统唯一知道的是所有GenericList对象都是其原始类型GenericList的对象,因此,上述instanceof运算如果改为如下 的形式就是正确的:
GenericList<Integer> []arr=(GenericList<Integer>[])new GenericList[10];
含上述语句的程序在编译时,编译器会发生警告信息,指出程序使用了未经过检查 或不安全的操作。产生该警告的原因是编译器不能确定上述语句中的强制类型转换是否能成功。如果读者想要了解更多的详细信息,就需要使用选项-Xlint:uncheck进行编译。
本文参考来源:Java程序设计第二版 刘慧宁等编制