详解java 中泛型中的类型擦除和桥方法

2022-10-25,

在java中,泛型的引入是为了在编译时提供强类型检查和支持泛型编程。为了实现泛型,java编译器应用类型擦除实现:

       1、  用类型参数(type parameters)的限定(如果没有就用object)替换泛型类型中的所有类型参数。

       2、  需要保持类型安全的时候插入类型转换(隐含插入)

       3、  在extened 泛型类型中生成桥方法来保证多态性

   类型擦除确保不会为已参数化了的类型(paramterized types)产生新类,这样泛型能保证没有运行时的负载。

泛型类型擦除

      在类型擦除过程中,java编译器擦除所有类型参数,用它的限定或者object(没限定时)替换。

     考虑下面的泛型类:

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类使用了限定类型参数:

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; } 
 // ... 

编译器会使用第一个限定类,comparable替换限定参数类型t:

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); 
 } 
} 

考虑以下代码:

mynode mn = new mynode(5); 
node n = mn;   // 原生类型 – 编译器会给出未检查警告 
n.setdata("hello");  
integer x = mn.data; // 会引发抛出classcastexception 

 类型擦除后,代码变成:

mynode mn = new mynode(5); 
node n = (mynode)mn;   //原生类型 – 编译器会给出未检查警告 
n.setdata("hello"); 
integer x = (string)mn.data; //会引发抛出classcastexception 
public class node { 
 
 public object data; 
 
 public node(object data) { this.data = data; } 
 
 public void setdata(object data) { 
  system.out.println("node.setdata"); 
  this.data = data; 
 } 
} 
 
public class mynode extends node { 
 
 public mynode(integer data) { super(data); } 
 
 public void setdata(integer data) { 
  system.out.println("mynode.setdata"); 
  super.setdata(data); 
 } 
} 

类型擦除后,方法的签名已经不匹配。node 方法变成setdata(object),mynode方法变成setdata(integer)。mynode setdata方法已经不是覆盖node setdata方法。

为了解决这个问题,维持泛型类型的多态性,java编译器会生成一个桥方法:

class mynode extends node { 
 
 // 编译器生成的桥方法 
 // 
 public void setdata(object data) { 
  setdata((integer) data); 
 } 
 
 public void setdata(integer data) { 
  system.out.println("mynode.setdata"); 
  super.setdata(data); 
 } 
 
 // ... 

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!