3 T smallest = a[0];
4 for(inti = 0; i < len; ++i) {
5 if(smallest.compareTo(a[i]) > 0)
6 smallest = a[i];
7 }
8 returnsmallest;
9 }
注:C++中的模板是在引用时才编译的,因此如果在模板类型中出现任何语法错误,但此时尚未有任何引用时,编译器是不会报错的。
4. 泛型代码中的类型擦除:记得在我阅读Thinking in Java 4th 的时候,书中给出了一些比较明确的解释,为什么Java会这样实现泛型,其最主要的原因是为了考虑向前兼容,也承认这样的实现方式有着很多的缺陷和弊病,希望Java在今后的版本中予以补足。
简单的说类型擦除,就是几乎所有的泛型相关的行为都是由编译器通过暗插各种各样的代码,或者是暗自修订部分代码的声明,然后再将修订后的代码(基本不再包含泛型信息)生成字节码后交给JVM去执行,因此可以据此判断在JVM中对我们的泛型类型是一无所知的。C++也是同样的道理,只是编译器完成的工作被定义为类型展开或类型实例化,因此,同样的模板类,如果实例化的类型参数不同,那么用他们声明出来的类对象也同样不属于相同类型的对象,其限制主要表现为,不能通过缺省copy constructor或者缺省赋值操作符来完成对象之间的复制,除非其中某个类型实例化后的对象专门针对另外一种类型实例化后的类型进行了copy constructor和赋值等于的重载。
1) 类型擦除:将类型参数替换为限定类型,如果没有限定类型则替换为Object,见如下代码:
1 publicclassPair
2 publicPair(T first,T second) { this.first = first; this.second = second; }
3 publicT getFirst() { returnfirst; }
4 publicT getSecond() { returnsecond; }
5 publicvoidsetFirst(T first) { this.first = first; }
6 publicvoidsetSecond(T second) { this.second = second; }
7 privateT first;
8 privateT second;
9 }
由于Pair中的类型参数T没有限定类型,因此类型擦除后将会变成如下代码:
1 publicclassPair {
2 publicPair(Object first,Object second) { this.first = first; this.second = second; }
3 publicObject getFirst() { returnfirst; }
4 publicObject getSecond() { returnsecond; }
5 publicvoidsetFirst(Object first) { this.first = first; }
6 publicvoidsetSecond(Object second) { this.second = second; }
7 privateObject first;
8 privateObject second;
9 }
因此尽管在调用Pair时,传递的类型参数有所不同,如String、Date,但是在类型擦除之后,他们将成为相同的类型。如果类型参数存在多个限定类型,则取第一个限定类型作为擦除后的类型参数,见如下代码:
1 publicclassInterval
2 publicInterval(T first, T second) {
3 if(first.compareTo(second) <= 0) {
4 lower = first;
5 upper = second;
6 } else{
7 lower = second;
8 uppper = first;
9 }
10 }
11 privateT lower;
12 privateT upper;
13 }
擦除类型信息后的原始类型如下:
1 publicclassInterval implementsSerializable {
2 publicInterval(Comparable first, Comparable second) {
3 if(first.compareTo(second) <= 0) {
4 lower = first;
5 upper = second;
6 } else{
7 lower = second;
8 uppper = first;
9 }
10 }
11 privateComparable lower;
12 privateComparable upper;
13 }
5. 泛型类向遗留代码的兼容:由于编译器自动完成了类型信息的擦除,因此在原有调用原始类型的地方,可以直接传入等价的泛型类,只要保证该泛型类在类型擦除后可以符合被调用函数参数的语法要求即可,见如下代码:
1 publicclassTestMain {
2 publicstaticvoidtest(MyClass t) {
3 System.out.println(t.getValue());
4 }
5
6 publicstaticvoidmain(String[] args) {
7 MyClass
8 test(v);
9 }
10 }
11 classMyClass
12 publicMyClass(T t) {
13 this.t = t;
14 }
15 publicT getValue() { returnt;}
16 privateT t;
17 }
6. 约束与局限性:
1) 不能使用原始类型作为类型参数,如int、double等,因为他们和Object之间没有直接的继承关系,因此在需要时只能使用包装类,如Integer、Double分别予以替换,不能这样的替换确实也带来了效率上的折损,C++