先了解Java的Generics:
根据Java的文档,Java引入Generics一是为了在编译时提供更强的类型检查,二是为了
泛型编程。
编译时,Java靠type erasure来实现Generics:
1. 将所有的泛型参数替换为限定(bound这里如何翻译?)的类型,如果没有限定,就替换为Object类。因此然生的字节码和普通的类文件一样;
2. 为了保证类型安全,必要的时候会是使用cast;
3. 为了维护继承的泛型类型之间的多态的正确性,必要的时候会生成bridge methods。
考虑下面的泛型类,这个类代表单链表的节点。(一下
例子均出自java doc)
public class Node<T> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) }
this.data = data;
this.next = next;
}
public T getData() { return data; }
// ...
}
因为T没有被限定,所以Java编译器将其替换为Object:
public class Node {
private Object data;
private Node next;
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
public Object getData() { return data; }
// ...
}
而下面这个例子,因为Node使用了一个父类型被限定的类型参数,所以T被替换为该父类型。
public class Node<T extends Comparable<T>> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
// ...
}
替换为:
public class Node {
private Comparable data;
private Node next;
public Node(Comparable data, Node next) {
this.data = data;
this.next = next;
}
public Comparable getData() { return data; }
// ...
}
这样貌似很不错,但有时候有问题,看这个例子:
public class Node<T> {
public T data;
public Node(T data) { this.data = data; }
public void setData(T data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node<Integer> {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
对于下面这段code,
MyNode mn = new MyNode(5);
Node n = mn;
n.setData("Hello");
Integer x = mn.data;
经过type erasure之后,code变成了下图中的样子(用Jad反编译的结果)
可以看到,多了一个方法,
public volatile void setData(Object obj)
{
setData((Integer)obj);
}
这个方法会将Object类型cast成Integer类型调用MyNode.setObject(Integer),被称为bridge method,因为父类Node有setData(Object)这个方法,如果子类没有,根本就无法编译通过。因为实际参数是个String对象,所以在做setData((Integer)obj)的时候会报
Exception in
thread "main" java.lang.ClassCastException: java.lang.String
cannot be cast to java.lang.Integer
at MyNode.setData(MyNode.java:1)
at MyNode.main(MyNode.java:11)
更多的关于Java Generics的文章可以参考附录【2】-【4】。
从type erasure的角度来看,Java的Generics真的只是Wrapper。
那么c++的模版呢?
摘自【2】里面的的说法:
Java 中的泛型与 C++ 模板的比较
Java Generics 程序的语法在表面上与 C++ 中的模板非常类似,但是二者之间有着本质的区别。
首先,Java 语言中的泛型不能接受基本类型作为类型参数――它只能接受引用类型。这意味着可以定义 List<Integer>,但是不可以定义 List<int>。
其次,在 C++ 模板中,编译器使用提供的类型参数来扩充模板,因此,为 List<A> 生成的 C++ 代码不同于为 List<B> 生成的代码,List<A> 和 List<B> 实际上是两个不同的类。而 Java 中的泛型则以不同的方式实现,编译器仅仅对这些类型参数进行擦除和替换。类型 ArrayList<Integer> 和 ArrayList<String> 的对象共享相同的类,并且只存在一个 ArrayList 类。
参考:
1. http://docs.oracle.com/javase/tutorial/java/generics/index.html
2. http://www.ibm.com/developerworks/cn/java/j-lo-gj/index.html
3. http://www.blogjava.net/DLevin/archive/2011/06/23/352917.html
4. http://freish.iteye.com/blog/1158008
- 大小: 102.1 KB