Java的Generics和c++的Template到底有什么不同?

本文详细解读了Java泛型的工作原理,包括类型擦除、类型安全和桥接方法等方面,并与C++模板进行了对比,强调了Java泛型在类型参数上的限制与实现方式的不同。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先了解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反编译的结果)

[img]http://dl.iteye.com/upload/attachment/0078/5134/64f07769-beb3-3faa-99db-def49dd3ddc3.png[/img]

可以看到,多了一个方法,

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. [url]http://docs.oracle.com/javase/tutorial/java/generics/index.html[/url]
2. [url]http://www.ibm.com/developerworks/cn/java/j-lo-gj/index.html[/url]
3. [url]http://www.blogjava.net/DLevin/archive/2011/06/23/352917.html[/url]
4. [url]http://freish.iteye.com/blog/1158008[/url]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值