JAVA泛型和C++泛型的区别:
Java的泛型被定义成擦除,而C++的泛型则是扩展;
对于C++模板,当参数类型为不同的类型时,生成的模板实例也是不同的类型,如:定义类模板
Template
当实例化模板时
A
A
这里的a和b是两种不同的类型的实例;
Java则不是这样的,如泛化List
List
List
通过反射机制看l和s的class,他们都是List!所有的参数类型都被编译器擦除了!
这样造成的结果是以往用C++模板可以实现某种契约(contract )的功能在Java中变得很另类了,举一个例子:
C++代码:
Template
Private:
T x;
Public:
Void func(){
int ret = x.foo(12);
}
}
上面这段代码在C++中经常能够看到,它暗中就设定了一个契约:凡是能否实例化这个模板的类型T,其必须具有一个公共的函数foo,这个函数返回整数,并且接受一个整数做为参数。
Java的代码:
public class A
private E e;
public void func(){
int ret = e.foo(12); //编译错误啊…
}
}
编译器给出的错误原因是foo函数对于类型中E是未定义的!!造成这样的问题的原因有两个:
C++的编译器直到模板被使用(实例化)的时候才去编译模板,如果你不去使用模板C++编译器不会编译模板;而实例化的时候编译器已经能够确定具体的参数类型,所以能够检测契约是否符合;Java的编译器不是这样工作的,所以它在编译模板类型的时候不能够确定E到底有没有这个foo函数;
类型擦除的结果;修改一下上面的程序,我们看看在func中到底能够调用什么函数,一看只能调用Object对象中的函数。所有的类型E都被擦除成Object了!
如果真的要想实现类似C++的契约,就必须确保参数类型E不被擦除成Object!需要如下修改代码:
public class A
private E e;
public void func(){
int ret = e.foo(12);
}
}
Class B{
Public int foo(int param){…};
}
这样虽然可以实现我们期望的形式,但是约束的程度要比C++的强很多,C++中,只要任意类型,其具有一个符合契约的函数,就可以实例化模板,而Java中,则要求所有的类型必须是给定类型的子类才可以实例化模板;
擦除的原则:
所有参数化容器类都被擦除成非参数化的(raw type);如List>都被擦除成List;
所有参数化数组都被擦除成非参数化的数组;如List
Raw type的容器类,被擦除成其自身,如List 被擦除成List;
原生类型(int,String还有wrapper类)都擦除成他们的自身;
参数类型E,被擦除成Object;
所有约束参数如 Extends E>、
如果有多个约束,擦除成第一个,如
例如:
泛化代码:
List words.add("Hello ");
words.add("world!");
String s = words.get(0)+words.get(1);
擦除后就变成了:
List words = new ArrayList();
words.add("Hello ");
words.add("world!");
String s = ( 擦除后的代码和以前没有泛型时候写的代码没有任何区别!
再例如:
泛化代码:
public class textReader
private T a;
public textReader(T b){
this.a = b;
}
public T getA(){
return a;
}
public static void main(String[] agrvs){
String in = "1234567890";
textReader
String out = test.getA();
System.out.println(out);
}
}
擦除后(所有类型参数都被去掉,T被擦除成Object)就变成(注意红色部分):
public class textReader{
private Object a;
public textReader(Object b){
this.a = b;
}
public Object getA(){
return a;
}
public static void main(String[] agrvs){
String in = "1234567890";
textReader test = new textReader (in);
String out = (String)test.getA();
System.out.println(out);
}
}
擦除所带来的问题:
静态成员共享问题
List ints = Arrays.asList(1,2,3);
List strings = Arrays.asList("one","two");
assert ints.getClass() == strings.getClass();
ints和strings两个对象最终被擦除成具有相同类型的(List)的对象,于是这两个对象共享List的静态成员,于是就可以得出这样的结论,所有泛化类型的静态成员被其所有的实例化对象共享,因此也就要求所有静态成员不能够是泛化的!
class Foo {
private final T value;
private public T getValue() { return value; }
public 2、 过载(overload)冲突问题函数过载的定义这样的:在一个类的范围内,如果两个函数具有相同的函数名称,不同的参数(返回值不考虑)就互相称为过载函数。看一个例子:
Class A{
Public int foo(int a){};
Public int foo(float f){}; // 是过载,编译没有问题
Public int foo(int a){};
Public float foo(int f){}; // 报错
Public static int foo1(List a){}
Public static int foo1(List s){} //编译有错误,因为所有的List都被擦除成List,这样两个函数重复定义,报错;
Public static int foo1(List a){}
Public static String foo1(List s){} //没有问题,编译器不会报错!
}
接口实现
一个类不能同时实现具有相同擦除效果的接口,例如:
class Foo implements Comparable, Comparable
继承关系:
原始继承:就是我们经常提到的继承关系,如ArrayList是List的子类;
泛化继承:
a) 泛化后的ArrayList 依旧是List的子类;其中T是参数化类型
b) 如果类型T是类型B的子类,那么List不是List的子类
c) List是List extends T>的子类
d) List是List extends B>的子类
e) List是List super T>的子类
f) List是List super T>的子类
g) 如果类型T是类型B的子类,那么T[]是B[]的子类
关于协变式(covariant)、不变式(invariant)和反变式(contravariant):
a) 数组和扩展类(extends)的泛化是协变式,即如果类型T是类型B的子类,那么T[]是B[]的子类;List是List extends B>的子类
b) 非扩展类泛型是不变式,即如果类型T是类型B的子类,那么List不是List的子类
c) Super类泛型是反变式,即如果B是T的超类,则List 是List super T>的子类
Get和Put原则:
当从一个泛化的结构中取数据的时候请使用extends通配,当往一个泛化的结构中放数据的时候请使用super通配;
当需要同时从一个泛化结构中读取和写入数据是,请不使用通配符号;
为什么会这样,我们简单的分析一下:假定类型T继承自A和B,类型C和D又从T类型继承,那么List extends T>中存放的只能是T类型或是C或是D类型,里面存放的类型都可以向上cast到T,所以从List extends T>中取东西编译器能够正确处理,只要映射到T就可以了(T是他们的父类)!往List extends T>放东西就不一样的,原来里面放的是T/C还是D都被擦除成T,所以编译器不知道原来到底存放的是什么类型,无法保证类型安全,所以这个操作被禁止!
List super T>中存放的是A/B或是T,往List super T>放T是允许的,因为T总是可以向上转换成A或是B,但是从里面取东西就有问题了,编译器还是不能够确定List里面放的是什么类型,可能是A也可能是B。
具体化:
当一个类型能够在运行时态被完整的表现,我们就称为其是可以具体化的,举一个例子:
List就不是一个可以具体化的类型,因为它在运行时态被擦除成List!所以当处理一些需要运行时检测类型的操作的时候(如instanceof)就要特别注意。
究竟哪些类型是可具体化的呢;
(1)、原始类型,如int;
(2)、非参数化的类和接口;
(3)、非限定的参数化类型,如List>, Map,?>
(4)、Raw类型,如 List,ArrayList等
(5)、所有由可具体化类型组成的数组,如Number[],List>[]等
哪些是不可具体化的类型呢
(1)、类型变量,如T;
(2),参数化类型,如List等
(3),有限定的参数化类型,如List extends Number>;
具体哪些操作需要注意区分是否是具体化类型:
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
软考中级精品资料免费领
- 历年真题答案解析
- 备考技巧名师总结
- 高频考点精准押题
- 资料下载
- 历年真题
193.9 KB下载数260
191.63 KB下载数245
143.91 KB下载数1139
183.71 KB下载数640
644.84 KB下载数2752
相关文章
发现更多好内容猜你喜欢
AI推送时光机Java泛型中<?>和<T>的区别浅析
后端开发2022-12-19Swift泛型Generics浅析讲解
后端开发2024-04-02深入浅析java中集合泛型的本质
后端开发2023-05-31Java使用反射获取list泛型过程浅析
后端开发2023-02-28Java泛型实例分析
后端开发2023-06-03Java 泛型详解(超详细的java泛型方法解析)
后端开发2024-04-02深入浅出理解Java泛型的使用
后端开发2024-04-02Java泛型使用实例分析
后端开发2023-06-29Java 泛型考古 泛型擦除 包装类详细解析
后端开发2024-04-02Java中泛型的示例分析
后端开发2023-06-20Java详细分析讲解泛型
后端开发2024-04-02详细全面解析Java泛型
后端开发2024-04-02Java基础泛型实例分析
后端开发2023-06-29Java深入浅出讲解泛型与包装类
后端开发2024-04-02浅谈Go1.18中的泛型编程
后端开发2022-06-07一篇文章带你了解java泛型--泛型类,泛型方法,泛型接口
后端开发2024-04-02 咦!没有更多了?去看看其它编程学习网 内容吧