语言中的泛型

java的设计采用了单根结构,除去在GC上的好处之外,在泛型编程上(模板C++),因为所有的类型都继承自Object,因此利用向上塑型,我们是可以写出如下的代码:

public class GenericTest {

public static void main(String[] args) {
List list = new ArrayList();
list.add("qqyumidi");
list.add("corn");
list.add(100);

for (int i = 0; i < list.size(); i++) {
String name = (String) list.get(i); // exception
System.out.println("name:" + name);
}
}
}
可以看到,不需要显示的模板声明,我们是可以把string和int都放入List里,但是问题是在向下塑型的过程中,如果把int转成string是不行的,java使用Exception来处理此种情况。

Java SE5之后也引入了参数化类型的概念,也就是泛型:
List<String> list = new ArrayList<String>();
list.add("qqyumidi");
list.add("corn");
//list.add(100); // 提示编译错误

这就是二者都遵循的泛型,但是java的泛型实现与c++是不同的:
public class GenericTest {

public static void main(String[] args) {

Box<String> name = new Box<String>("corn");
Box<Integer> age = new Box<Integer>(712);

System.out.println("name class:" + name.getClass()); // com.qqyumidi.Box
System.out.println("age class:" + age.getClass()); // com.qqyumidi.Box
System.out.println(name.getClass() == age.getClass()); // true

}

}
可以看到,在运行时,不同的泛型实际上是用一个类型,同样意义的代码在c++里完全是两回事情:
在于Java中的泛型这一概念提出的目的,导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。

原始类型(raw type)就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型。无论何时定义一个泛型类型,相应的原始类型都会被自动地提供。类型变量被擦除(crased),并使用其限定类型(无限定的变量用Object)替换。

Java代码  

1. class Pair<T> {    
2.   private T value;    
3.   public T getValue() {    
4.     return value;    
5.   }    
6.   public void setValue(T  value) {    
7.     this.value = value;    
8.   }    
9. }    
 Pair<T>的原始类型为:
Java代码  

1. class Pair {    
2.   private Object value;    
3.   public Object getValue() {    
4.     return value;    
5.   }    
6.   public void setValue(Object  value) {    
7.     this.value = value;    
8.   }    
9. }  

 

编译器如何处理泛型?
     通常情况下,一个编译器处理泛型有两种方式:
     1.Code specialization。在实例化一个泛型类或泛型方法时都产生一份新的目标代码(字节码or二进制代码)。例如,针对一个泛型list,可能需要 针对string,integer,float产生三份目标代码。
     2.Code sharing。对每个泛型类只生成唯一的一份目标代码;该泛型类的所有实例都映射到这份目标代码上,在需要的时候执行类型检查和类型转换。
     C++中的模板(template)是典型的Code specialization实现。C++编译器会为每一个泛型类实例生成一份执行代码。执行代码中integer list和string list是两种不同的类型。这样会导致代码膨胀(code bloat),不过有经验的C++程序员可以有技巧的避免代码膨胀。
     Code specialization另外一个弊端是在引用类型系统中,浪费空间,因为引用类型集合中元素本质上都是一个指针。没必要为每个类型都产生一份执行代码。而这也是Java编译器中采用Code sharing方式处理泛型的主要原因。
     Java编译器通过Code sharing方式为每个泛型类型创建唯一的字节码表示,并且将该泛型类型的实例都映射到这个唯一的字节码表示上。将多种泛型类形实例映射到唯一的字节码表示是通过类型擦除(type erasue)实现的。

 

posted @ 2017-05-02 08:30  llluiop  阅读(196)  评论(0编辑  收藏  举报