java泛型和c++模板的区别
文章目录
主要介绍java泛型,再与C++模板作一个简单的比较。
1 Java的泛型(Generic Type)
Java泛型基于类型擦除,使用了泛型的代码在编译后看不到“泛型语法”,泛型可以理解为语法糖,是写给编译器看的,虚拟机里没有泛型的概念。
1.1 为什么要使用泛型
java
设计之初并没有泛型,比如,在jdk1.5
以前,ArrayList
操作的对象都是Object
,使用时需要进行手动转换。
举个例子,我们构造一个数组,用来保存String
类型的字符串,然后打印每一个字符串的长度:
|
|
输出结果:
|
|
然后我们再往数组中添加一个元素,但是输入数据时少打了个双引号,原本想要输入字符串却输入了一个整数:
|
|
重新运行这段代码,输出结果:
|
|
从结果可以看到,在循环运行时,前面3次都正确打印出了结果,而到第4次执行到第11行-类型转换时出错了,因为我们在对应位置存储的是一个整数,而不是字符串,这是一个运行时错误(RuntimeException)
。
通过上面的例子,我们可以看到“手动转换”来保证类型正确的弊端:
- 每次取出数据都要手动转换,这一点在编译时可以检查
- 没法在编译时检查数据输入的合法性,一旦出错,就是
RuntimeException
引入泛型以后,上面的例子就简单多了:
|
|
泛型让你的代码简单又可靠。
1.2 泛型实现–类型擦除
泛型在编译的时候,具体的类型参数会被擦除,取而代之的是它的第一个“限定类型”,也就是其继承的类或者实现的接口,如果都没有的话,自然就是Object
类了。编译器会在编译时检查你的代码,如果有错误的类型转换出现(如给泛型List
添加一个错误类型的对象或者把从List
取出的对象赋值给一个错误类型的变量),则会报错,无法通过编译。所以泛型实际上是把运行时的类型转换检查提前到了编译时,避免运行时异常。
所以,在判断泛型所属类型的时候,不管你传入参数的是什么类型,泛型始终都是同一个类型:
|
|
PS:虽然进行了“类型擦除”,
class
文件中仍然有关于被擦除类型的信息,被保存在LocalVariableTypeTable
中。
关于“限定类”,有几点要注意:
- 可以是一个或者多个"限定类型",但是最多只能有一个是类,其它的必须是接口,这跟类继承是一样的,但是用
&
符号连接,因为逗号用来分隔不同的类型参数 - 如果有一个是类,那么类写在第一个位置,如
<T extends ClassA&InterfaceB&InterfaceC>
- 如果全部是接口,把最复杂的接口写在最前面可以提高效率。因为类型擦除是转换成第一个"限定类型",如果第一个只是一个标记接口,那么后面用到其它接口的地方,还要再加入强制转换。
1.3 泛型的一些限制
泛型有一些应用限制,其中一部分就是因为类型擦除。
- 不能传入8大原始类型。
int,short,long,byte,boolean,double,float,char
- 泛型中的类型参数不能被实例化,但是可以通过反射,用
Class<T>
的newInstance()
方法获得实例(JDK9
以后官方推荐用Class<T>.getDeclaredConstructor().newInstance()
) - 泛型中的类型参数不能被声明为静态的
- 不能对带有类型参数的泛型表达式使用
instance of
,(大部分时候)也不能进行类型转换 - 不能创建泛型数组,
Object[] stringLists = new List<String>[]; // compiler error
- 泛型类不能继承异常类型,如
AException<T> extends Throwable//compile error
,也不能捕捉T,但是可以throws T
- 不能重载一个仅仅是泛型参数类型不同的方法,因为类型擦除后,他们是一样的。
2 C++模板(template)
c++的模板就跟它的名字一样,定义的时候它是一个模板,后面使用时,编译器会把模板中的类型替换为具体的类型,生成一份“根据模板制作”的代码。c++有两种模板,类模板和函数模板,模板本身不是类或函数,可以理解创建类或函数的公式。
2.1 模板语法
以函数模板举例:
|
|
3 区别
3.1编译阶段的处理方式不同
java 在编译时直接把泛型中的类型参数擦除,并在需要地方插件强制类型转换。而c++在编译到模板定义的代码时,并没有任何操作,只在使用到这份模板代码时,根据实际类型,为这个类型生成一份特定的代码。这一点其实是本质区别,后面的几点区别都是因为这个。
3.2可以接收的参数不同
java 泛型不能接收8大原始类型,c++没有这个限制。java在类型擦除时,把类型“退化”成它的限定类型,而原始类型比较特殊,无法“退化”成Object
。java可以接收通配符,c++不能。c++不能接收类型限定,虽然可以通过其它手段,如c++20
的concept
来实现类似的功能。
3.3 效率不同
java的类型擦除会增加很多类型转换的操作,效率低下。而c++模板生成一份特定类型的代码,这跟你自己手写一份和“模板”逻辑一样的代码没有什么区别。
4 总结
java选择这种看起来缺点很多的泛型实现方式,根本原因就是为了一个优点“兼容性”。java泛型和c++模板本质上还是差别挺大的,c++的模板像宏,java泛型像语法糖。
版权声明 本博客使用CC BY-NC-SA 4.0许可协议(创意共享4.0:保留署名-非商业性使用-相同方式共享)。