擦除
- Java中泛型通过擦除实现。泛型类型只在静态静态类型检查期间存在,此后将擦除至其非泛型上界。
- 可以使用Class.getTypeParameters()获取类型参数(由于擦除->无法获取任何关于泛型参数类型的信息)
- 边界动作:
- 进入对象时(调用方法)-- 编译器执行编译时期的类型检查
- 离开对象时(方法返回)-- 编译器自动插入类型转换代码
- 弥补对泛型参数类型信息的擦除–传入类型标签(Class对象)(由编译器保证类型标签可以匹配泛型参数)
class Foo<T>{
private Class<T> type;
Foo(Class<T> type){
this.type = type;
}
}
- 无法直接创建泛型数组。只能通过创建一个被擦除类型的数组,然后对其转型
static class GenericArray<T>{
private T[] array;
//压制 unchecked cast警告
@SuppressWarnings("unchecked")
public GenericArray(int n){
//无法直接创建泛型数组: this.arry = new T[n];
this.array = (T[])new Object[n];
}
public T[] rep(){
return array;
}
public T get(int index){
return array[index];
}
public void put(int index, T value){
array[index] = value;
}
}
- 一个类不能实现同一个泛型接口的两种变体(由于擦除,相当于实现两次相同接口)
- 由于擦除,不同泛型参数的类型不能用于重载
- 由于擦除,catch语句不能捕获泛型类型的异常。泛型类也不能直接或间接继承自Throwable
边界
-
使用通配符限制边界
-
- 规定上界: <? extends Class1> 表示Class1或Class1派生出的类
- Generic<? extend Class1> 可以指向 Generic< 由Class1派生出的类 >
- 对Generic<? extend Class1>不能调用 参数列表 任何涉及通配符的方法 – 因为无法知道传入的参数的是否是可以被?指向
- 可以调用返回值涉及通配符的方法,将返回Class1类型 – 擦除至此,?一定是Class1或其派生类
- 规定下界: <? super Class1> 表示Class1或Class1的超类
- Generic<? super Class1> 可以指向 Generic< 由Class1的任何超类 >
- 对Generic<? super Class1>可以调用 参数列表 涉及统配符的方法,传入Class1的派生类 – ?是Class1的基类,可以指向其派生类
- 可以调用返回值涉及通配符的方法, 将返回Object类型 – 无法确定其具体的上界
- 规定上界: <? extends Class1> 表示Class1或Class1派生出的类
自限定类型
public class SelfBounded<T extends SelfBounded<T>>{
}
//合法的继承
public class A extends SelfBounded<A>{
}
public class B extends SelfBounded<A>{
}
- SelfBounded的泛型参数T 要求继承自SelfBounded – A B符合
- 自限定及要求在继承中如此使用该类:
public class A extends SelfBounded<A>{}
- 方法参数协变 通过自限定类型实现参数类型协变 (返回类型协变JavaSE5引入)
interface SelfBounded<T extends SelfBounded<T>>{
void set(T t);
}
interface A extends SelfBounded<A>{ }
混型
- 使用接口
interface A{ }
class AImpl implements A{ }
interface B{ }
class BImpl implements B{ }
interface Basic{ }
class BasicImpl implements Basic{ }
//将A B 混入Basic
class Mixin extends BasicImpl implements A, B{
private A a = new AImpl();
private B b = new BImpl();
}
- 使用装饰器模式
- 使用动态代理
- InvocationHandler内,建立 函数名 到 混入类对象 的映射
- 在该代理对象上调用方法时,通过该映射找到来自那个混入的类,再在其对象上调用
潜在类型机制
泛型类型必须是某个类的派生类。可以横跨类的继承结构,不关心泛型参数是什么类型,只要其拥有规定的接口。(C++的泛型)
- Java本身的泛型不支持潜在类型,但可以使用反射来实现潜在类型机制
- 通过方法名、参数列表获取类的Method对象,进行调用
class A{
public void func();
}
class B{
public void func();
}
class Test{
//不规定obj是什么类的对象,只要求其拥有public的func()
void callFunc(Object obj){
try{
Method method = obj.getMethod("func");
method.invoke(obj);
}catch(NoSuchMethodException e){
//...
}
}
}