泛型
没有泛型类的时候, 泛型程序用继承实现, ArrayList
类只维护一个Object
引用数组, 这样就会每次都需要进行强制类型转换
现在是使用尖括号<String>
Java
类库使用E
表示集合类型, K,V
表示键值对, T,U
等表示任意类型
泛型类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Pair <T> { T first; T second; public Pair () { first = null ; second = null ; } public Pair (T first, T second) { this .first = first; this .second = second; } public T getFirst () { return first; } public T getSecond () { return second; } public void setFirst (T newV) { first = newV; } public void setSecond (T newV) { second = newV; } }
泛型方法
1 2 3 4 5 class A { public static <T> T getMiddle (T...a) { return a[a.length / 2 ]; } }
泛型代码和虚拟机
无论何时定义一个泛型类, 都会自动提供一个相应的原始类型, 就是去掉类型参数以后的泛型类型名
类型变量会被擦除, 替换为其限定类型, 如果没有限定类型就是Object
泛型方法的类型擦除会存在一些问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class D extends Pair <LocalDate> { public void setSecond (LocalDate second) { if (second.compareTo(getFirst()) >= 0 ) { super .setSecond(second); } } ... } class D extends Pair { public void setSecond (LocalDate second) {...} ... } public void setSecond (Object second) {}public void setSecond (Object second) {setSecond((LocalDate) second);}
1 2 3 4 5 6 7 public LocalDate getSecond () ;public Object getSecond () ;
虚拟机中没有泛型, 只有普通的类和方法
所有的类型参数都会替换为他们的限定类型
会合成桥方法来保持多态
会插入强制类型转换来保持类型安全性
限制
不能使用基本类型实例化类型参数
不能使用Pair<double>
, 因为在类型擦除后的Object
类中不能存储基本类型, 只能存储Double
运行时类型查询只适用于原始类型 1 2 3 4 5 6 7 8 if (a instanceof Pair<String>) if (a instanceof Pair<T>) Pair<String> p = (Pair<String>) a; Pair<String> stringp = ...; Pair<E> ep = ...; if (stringp.getClass() == ep.getClass()) {}
不能创建参数化类型的数组 1 2 3 4 5 6 7 8 var table = new Pair <String>[10 ]; var table = (Pair<String>[]) new Pair <?>[10 ];
Varargs
警告
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static <T> void addAll (Collection<T> coll, T... ts) { for (T t : ts) { coll.add(t); } } Collection<Pair<String>> table = ... Pair<String> p1 = ... Pair<String> p2 = ... addAll(table, p1, p2);
@SafeVarargs
注解只能用于static, final
或private
的构造器或方法中, 其他方法都可能被覆盖, 会使这注解失去作用
不能实例化类型变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class Pair <T> { private T first; private T second; public Pair () { first = new T (); second = new T (); } } Pair<String> p = Pair.makePair(String::new ); public static <T> Pair<T> makePair (Supplier<T> constr) { return new Pair <>(constr.get, constr.get()); } first = T.class.getConstructor().newInstance(); public static <T> Pair<T> makePair (Class<T> cl) { try { return new Pair <>(cl.getConstructor.newInstance(), cl.getConstructor().newInstance()); } catch (Exception e) { return null ; } } Pair<String> p = Pair.makePair(String.class);
不能构造泛型数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public static <T extends Comparable > T[] minmax(T... a) { T[] mm = new T [2 ]; ... } public class ArrayList <E> { private Object[] elements; ... @SuppressWarnings("unchecked") public E get (int n) { return (E) elements[n]; } public void set (int n, E e) { elements[n] = e; } } public static <T extends Comparable > minmax(T... a) { var result = new Comparable [2 ]; ... return (T[]) result; } String[] names = A.minmax("T" , "A" , "N" ); public static <T extends Comparable > T[] minmax(IntFunction<T[]> constr, T... a) { T[] result = constr.apply(2 ); ... } String[] names = A.minmax(String::new , "T" , "A" , "N" ); public static <T extends Comparable > T[] minmax(T... a) { var result = (T[]) Array.newInstance(a.getClass().getComponentType(), 2 ); ... }
泛型类的静态上下文中类型变量无效
1 2 3 4 5 6 7 8 public class S <T> { private static T si; public static T getSi () { if (si == null ) construct new instanceof T; return si; } }
不能抛出或捕获泛型类的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Problem <T> extends Exception {...} public static <T extends Throwable > void doWork (Class<T> t) { try {...} catch (T e) { ... } } public static <T extends Throwable > void doWork (T t) throws T { try {...} catch (Throwable realCause) { t.initCause(realCause); throw t; } }
可以取消对检查型异常的检查
正常必须对所有检查型异常提供一个处理器, 但是可以利用泛型取消这个检查
只需要使用泛型类, 类型擦除和@SuppressWarnings("unchecked")
1 2 3 4 5 6 7 8 9 10 11 @SuppressWarnings("unchecked") static <T extends Throwable > void throwAs (Throwable t) throws T { throw (T) t; } Task.<RuntimeException>throwAs(e); try {...} catch (Throwable t) { Task.<RuntimeException>throwAs(t); }
擦除以后可能会存在冲突
1 2 3 4 5 6 7 public class Pair <T> { public boolean equals (T value) { return first.equals(value) && second.equals(value); } }
假如两个接口类型是同一个接口的不同参数化, 一个类或者类型变量就不能同时作为这两个接口类型的子类
1 2 3 class E implements Comparable <E> {...}class M extends E implements Comparable <M> {...}
泛型继承规则
如果M
是E
的子类, Pair<M>
不会是Pair<E>
的子类
1 Pair<E> b = new Pair <M>(1 , 2 );
通配符类型
Pair<? extends E>
允许类型参数变化, 表示任何Pair
类型, 只要是E
的子类
Pair<M>
是Pair<? extends E>
的子类型
可以使用? super M
指定一个超类型限定, 限制为M
的所有超类型
带超类限定的通配符允许写入一个泛型对象, 带子类限定的通配符允许读取一个泛型对象
Comparable
接口本身就是一个泛型类型
1 2 3 4 5 6 public interface Comparable <T> { public int compareTo (T other) ; } public int compareTo (String other) ;public static <T extends Comparable <? super T>> pair<T> minmax (T[] a)
无限定通配符Pair<?>
Pair<?>
和Pair
本质区别在于可以使用任意的Object
对象调用原始Pair
类的setFirst()
测试一个Pair
是否包含Null
引用, 不需要具体的类型
1 2 3 4 5 6 public static boolean hasNulls (Pair<?> p) { return p.getFirst() == null || p.getSecond() == null ; } public static <T> boolean hasNulls (Pair<T> p) ;
交换Pair
元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static void swap (Pair<?> p) ;? t = p.getFirst(); public static <T> void swapHelper (Pair<T> p) { T t = p.getFirst(); p.setFirst(p.getSecond()); p.setSecond(t); } public static void swap (Pair<?> p) {swapHelper(p);}public static void minmaxB (M[] a, Pair<? super M> res) { minmaxB(a, res); PairAlg.swapHelper(res); }
反射和泛型
泛型Class
类
Class
类是泛型类, String.class
实际上是Class<String>
类的对象
类型参数非常有用, 允许Class<T>
的方法有更加特定的返回类型 1 2 3 4 5 6 7 8 9 10 11 T newInstance () ; T cast (Object obj) ; T[] getEnumConstants(); Class<? super T> getSuperclass(); Constructor<T> getConstructor (Class... parameterTypes) ; Constructor<T> getDeclaredConstructor (Class... parameterTypes) ;
使用Class<T>
参数进行类型匹配
1 2 3 4 5 public static <T> Pair<T> makePair (Class<T> c) throws InstantiationException, IllegalAccessException { return new Pair <>(c.newInstance(), c.newInstance()); }