一.开篇
在学习泛型的过程中,有许多零零碎碎的知识点,它们并不好用单独一篇文章来说,遂罗列如此。
二.泛型异常
- catch语句不能捕获泛型类型的异常,因为在编译器和运行期间都必须知道异常的确切类型。
- 泛型类不能直接或间接继承Throwable,这并不是说泛型形式类型参数不能继承Throwable。
package com.jyz.study.jdk.generic; /** * 泛型在异常中的限制 * @author JoyoungZhang@gmail.com * */ public class GenericException<T extends Throwable> { void test(T t) throws T{//T被擦除到了Throwable, 可以声明形式类型参数这个异常 try{ throw t; //T被擦除到了Throwable,这里也可以编译通过 }catch(T ex){ //compile error 1.catch块不能捕获泛型类型的异常 //因为在编译器和运行期间都必须知道异常的确切类型,即使知道T被擦除到了Throwable也没用 } } void erasureTest(Throwable t) throws Throwable { try{ throw t; }catch(Throwable ex){ } } } //compile error //2.泛型类不能直接或间接继承Throwable //并不是说泛型形式类型参数不能继承Throwable class MyException<T extends Throwable> extends Throwable{ }
三.泛型数组
- 数组是协变的(covarant), Super[]是Sub[]的父类型。
- 泛型是不可变的invariant, List<Super>跟List<Sub>没有任何关系。
- 以上两点决定了数组和泛型不能很好的混用,如创建 泛型数组,参数化类型的数组,类型参数的数组都是非法的。更多知识点参考代码。
package com.jyz.study.jdk.generic; import java.util.ArrayList; /** * 数组是协变的covarant, Super[]是Sub[]的父类型 * 泛型是不可变的invariant, List<Super>跟List<Sub>没有任何关系 * @author JoyoungZhang@gmail.com * */ public class ArrayAndList<T> { void test1(){ Number[] objectArray = new Long[1]; objectArray[0] = 10.10;//运行时java.lang.ArrayStoreException: java.lang.Double } void test2(T t){ // List<Number> list = new ArrayList<Long>();//comiple error } //数组和泛型不能很好的混用 //如创建 泛型数组,参数化类型的数组,类型参数的数组都是非法的 void illegal(Object object){ //可以按编译器喜欢的方式定义一个泛型数组的引用 List<String>[] justAReference; // //but 永远是一个引用,不能被实例化 // justAReference = new ArrayList<String>()[]; // new ArrayList<T>()[]; // new T[10]; // //以下也不能工作 // obj instanceof T // new T(); } public static void main(String[] args) { System.out.println(new ArrayList<Number>().getClass()); System.out.println(new ArrayList<Integer>().getClass()); } }
- 但我们仍然可以有自己的方式创建泛型数组。三种方式参考Thinking in Java P384-386。第三者方式稍作修改如下。
package com.jyz.component.core.collection; import java.lang.reflect.Array; import java.util.Arrays; /** * 泛型数组 * from Thinking in Java page 385 * @author JoyoungZhang@gmail.com * */ public class JyzArray<T> { private T[] array; /** * * @param clazz 从擦除中恢复类型,使得我们可以创建需要的实际类型的数组 * 该数组的运行时类型就是确切类型T[],而不是擦除后的Object * @param length 数组长度 */ @SuppressWarnings("unchecked") public JyzArray(Class<T> clazz, int length){ array = (T[]) Array.newInstance(clazz, length); } /** * put item to index * @param index * @param item * @throws IndexOutOfBoundsException */ public void put(int index, T item){ RangeCheck(index, array.length); array[index] = item; } /** * * @param index * @return * @throws IndexOutOfBoundsException */ public T get(int index){ RangeCheck(index, array.length); return array[index]; } /** * * @return 泛型数组 */ public T[] rep(){ return array; } private void RangeCheck(int index, int length) { if (index >= length) { throw new IndexOutOfBoundsException("Index: "+index+", Size: "+length); } } public static void main(String[] args) { JyzArray<Integer> jyzArray = new JyzArray<Integer>(Integer.class, 8); for(int i=0; i<8; i++){ jyzArray.put(i, i); } Integer[] array = jyzArray.rep(); System.out.println(Arrays.toString(array)); } }
四.泛型重载
void f(List<T> v){}
void f(List<TT> v){}
将产生相同的方法签名,编译不通过。
五.自限定异常
- SelfBounded类接受形式类型参数T,而T有一个边界类限定,这个边界就是拥有T作为形式类型参数的SelfBounded。
- 古怪的循环泛型(CRG)本质:基类用导出类作为其实际类型参数。
public class SelfBounded<T extends SelfBounded<T>> { } //基类用导出类作为其实际类型参数 class GenericType<T>{} class CuriouslyRecurringGeneric extends GenericType<CuriouslyRecurringGeneric>{}