虚拟机中没有泛型,只有普通的类和方法。
类型擦除会将类型参数替换成相应的限定类型,如果没有限定类型则替换为Object。
桥方法主要用来解决类型擦除和多态特性的冲突问题。
举例:
定义一个泛型类Pair
public class Pair<T> {
private T value;
public Pair() {}
public Pair(T value) {
this.value= value;
}
public T getValue { return value; }
public void setValue(T newValue) { value = newValue; }
}
该类因为没有限定类型,所以在类型擦除后的原始类型如下:
public class Pair {
private Object value;
public Pair() {}
public Pair(Object value) {
this.value= value;
}
public Object getValue { return value; }
public void setValue(Object newValue) { value = newValue; }
}
定义如上Pair
public class Interval extends Pair<LocalDate> {
@Override
public void setValue(LocalDate value){
super.setValue(second);
}
}
如上子类进行类型擦除后的原始类型如下:
public class Interval extends Pair {
@Override
public void setValue(LocalDate value){
super.setValue(second);
}
}
在执行如下main方法时会存在问题:
public static void main(String[] args) {
Interval interval = new Interval();
// 父类引用指向子类对象
Pair<LocalDate> pair = interval;
pair.setValue(LocalDate.now());
}
父类引用pair指向子类对象interval,在调用setValue()方法时,应该首先调用子类重写父类的setValue方法,但是:
Interval类中的setValue方法参数类型为LocalDate,而父类中的setValue方法参数类型擦除后为Object,这就造成了类型擦除与多态特性的冲突。
所以Interval类中会生成如下桥方法:
public void setValue(Object newValue) {
setValue((LocalDate) newValue);
}
泛型约束和局限性
-
不能用基本类型实例化类型参数:类型擦除后变为Object,Object不能存储基本类型的值。
-
运行时类型查询只适用于原始类型。
查询一个对象是否属于某个泛型类型时,使用instanceof会得到编译器错误,如果使用强制类型转换得到一个警告。如下:
if (a instanceof Pair<String>) // Error
if (a instanceof Pair<T>) // Error
Pair<St「ing> p = (Pair<String>) a; // Warning-can only test that a is a Pair
getClass方法总是返回原始类型。如下:
Pair<String> stringPair = . .
Pai「<Employee〉employeePair = . .
// 返回的都是Pair.class
if (stringPair.getClassO == employeePair.getClassO) // they are equal
- 不能实例化参数化类型的数组。如下:
Pair<String>[] table = new Pair<String>[10]; // Error
但是声明参数化类型数组的变量是可以的。如下:
Pair<String>[] table;
解决方法:
Pair<String>[] table = (Pair<String>[]) new Pair<?>[10];
- 不能实例化类型变量:不能使用像 new T(...),new T[...] 或 T.class这样的表达式中的类型变量。如下:
public Pair() { first = new T(); second = new T(); } // Error
// 解决方法如下:
Pair<String> p = Pair.makePairCString::new);
public static <T> Pair<T> makePair(Supplier<T> constr) {
return new Pair<>(constr.get0. constr.get0); }
- 不能构造泛型数组:就像不能实例化一个泛型实例一样,也不能实例化数组。
public static <T extends Comparable〉T[] minmax(T[] a) { T[] mm = new T[2]; . . . } // Error
解决方法如下:
public class Test {
/*public static <T extends Comparable> T[] minmax(T[] a) {
Object[] mm = new Object[2];
return (T[]) mm;
}*/
public static <T extends Comparable> T[] minmax(IntFunction<T[]> constr, T... elements) {
T[] mm = constr.apply(2);
if (elements.length <= 2 && elements.length > 0) {
return mm = elements;
}
return (T[]) mm;
}
public static void main(String[] args) {
String[] strings = new String[2];
String[] mm = minmax(String[]::new, "hello", "world");
}
}
- 泛型类静态上下文中的类型变量无效
public class Singleton<T> {
private static T singlelnstance; // Error
public static T getSinglelnstanceO // Error
{
if (singleinstance == null) construct new instance of T
return singlelnstance;
}
}
- 不能抛出或捕获泛型类的实例:既不能抛出也不能捕获泛型类对象,实际上甚至泛型类扩展Throwable都是不合法的。
错误示例:
public class Problem<T> extends Exception { /* . . . */ } // Error can't extend Throwable
// catch子句中不能使用类型变量
public static <T extends Throwable〉void doWork(Class<T> t) {
try
{
do work
}
catch (T e) // Error can 't catch type variable
{
Logger,global.info(...)
}
}
在异常规范中使用类型变量是允许的。
public static <T extends Throwable〉void doWork(T t) throws T // OK
{
try {
do work
}
catch (Throwable real Cause)
{
t.initCause(real Cause);
throw t;
}
}
- 擦除后的冲突:要想支持擦除的转换,就需要强行限制一个类或类变量不能同时成为两个接口类型的子类,而这两个接口是同一接口的不同参数化,
// Manager 会实现 Comparable<Employee> 和 Comparable<Manager>, 这是同一接口的不同参数化。
class Employee implements Coinparab1e<Emp1oyee> { . . . }
class Manager extends Employee implements Comparable<Hanager> { . . . } // Error
- 泛型类型继承规则
例如:Manager是Employee的子类,但是Pair不是Pair 的子类。