泛型的多态
我们的本意是进行重写,实现多态。可是类型擦除后,只能变为了重载。这样,类型擦除就和多态有了冲突。
桥方法的参数类型都是Object,也就是说,子类中真正覆盖父类两个方法的就是这两个我们看不到的桥方法。而打在我们自己定义的setvalue
和getValue
方法上面的@Oveerride
只不过是假象。而桥方法的内部实现,就只是去调用我们自己重写的那两个方法。
所以,虚拟机巧妙的使用了桥方法,来解决了类型擦除和多态的冲突。
基本类型不能作为泛型类型
因为当类型擦除后,ArrayList的原始类型变为Object,但是Object类型不能存储int值,只能引用Integer的值。
另外需要注意,我们能够使用list.add(1)
是因为Java基础类型的自动装箱拆箱操作。
泛型类型不能实例化
如下代码会在编译器中报错:
T test = new T(); // ERROR
因为在 Java 编译期没法确定泛型参数化类型,也就找不到对应的类字节码文件,所以自然就不行了,此外由于T
被擦除为 Object
,如果可以 new T()
则就变成了 new Object()
,失去了本意。
可以通过反射实现:
static <T> T newTclass (Class < T > clazz) throws InstantiationException, IllegalAccessException {
T obj = clazz.newInstance();
return obj;
}
泛型数组
Java 的泛型数组初始化时数组类型不能是具体的泛型类型,只能是通配符的形式,因为具体类型会导致可存入任意类型对象,在取出时会发生类型转换异常,会与泛型的设计思想冲突,而通配符形式本来就需要自己强转,符合预期。
错误做法:
List<String>[] lsa = new List<String>[10]; // Not really allowed.
String s = lsa[1].get(0); // Run-time error ClassCastException.
建议做法:
List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type.
Integer i = (Integer) lsa[1].get(0); // OK
在 Java 中是不能创建一个确切的泛型类型的数组的,除非是采用通配符的方式且要做显式类型转换才可以。
List<String>[] list13 = (List<String>[]) new ArrayList<?>[10]; //OK,但是会有警告
List<?>[] list15 = new ArrayList<?>[10]; //OK
List<String>[] list6 = new ArrayList[10]; //OK,但是会有警告
使用反射来初始化泛型数组算是优雅实现:
public class ArrayWithTypeToken<T> {
private T[] array;
public ArrayWithTypeToken(Class<T> type, int size) {
array = (T[]) Array.newInstance(type, size);
}
public void put(int index, T item) {
array[index] = item;
}
public T get(int index) {
return array[index];
}
public T[] create() {
return array;
}
}
//...
ArrayWithTypeToken<Integer> arrayToken = new ArrayWithTypeToken<Integer>(Integer.class, 100);
Integer[] array = arrayToken.create();
静态方法和静态变量
泛型类中的静态方法和静态变量不可以使用泛型类所声明的泛型类型参数。
public class Test2<T> {
public static T one; //编译错误
public static T show(T one){ //编译错误
return null;
}
}
因为泛型类中的泛型参数的实例化是在定义对象的时候指定的,而静态变量和静态方法不需要使用对象来调用。对象都没有创建,如何确定这个泛型参数是何种类型,所以当然是错误的。
但是要注意区分下面的一种情况:
public class Test2<T> {
public static <T> T show(T one){ //这是正确的
return null;
}
}
因为这是一个泛型方法,在泛型方法中使用的T是自己在方法中定义的 T,而不是泛型类中的T。
异常
不能抛出也不能捕获泛型类的对象。
不能再catch子句中使用泛型变量。
但是在异常声明中可以使用类型变量。下面方法是合法的:
public static<T extends Throwable> void doWork(T t) throws T {
try{
...
} catch(Throwable realCause) {
t.initCause(realCause);
throw t;
}
}
获取泛型的参数类型
既然类型被擦除了,那么如何获取泛型的参数类型呢?可以通过反射(java.lang.reflect.Type
)获取泛型。
java.lang.reflect.Type
是Java中所有类型的公共高级接口, 代表了Java中的所有类型. Type体系中类型的包括:数组类型(GenericArrayType
)、参数化类型(ParameterizedType
)、类型变量(TypeVariable
)、通配符类型(WildcardType
)、原始类型(Class
)、基本类型(Class
), 以上这些类型都实现Type接口。
public class GenericType<T> {
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public static void main(String[] args) {
GenericType<String> genericType = new GenericType<>();
Type superClass = genericType.getClass().getGenericSuperclass();
//getActualTypeArguments 返回确切的泛型参数, 如Map<String, Integer>返回[String, Integer]
final Type actualTypeArgument = ((ParameterizedType) superClass).getActualTypeArguments()[0];
System.out.println(actualTypeArgument);
}
}
public interface ParameterizedType extends Type {
// 返回确切的泛型参数, 如Map<String, Integer>返回[String, Integer]
Type[] getActualTypeArguments();
//返回当前class或interface声明的类型, 如List<?>返回List
Type getRawType();
//返回所属类型. 如,当前类型为O<T>.I<S>, 则返回O<T>. 顶级类型将返回null
Type getOwnerType();
}