public class GenericConstructor { public static void main(String[] args) { // 泛型构造器中的T参数为String new Foo("Java learning"); // 泛型构造器中的T参数为Integer new Foo(200); // 显式指定泛型构造器中的T参数为String // 传给Foo构造器的实参也是String对象,完全正确 new <String>Foo("Go Programming"); }}class Foo { public <T> Foo(T t) { System.out.println(t); }}
public static <T> void copy()(Collection<T> dest, Collection<? extends T> src) { for(T ele : src) { dest.add(ele); } }
假设该方法需要一个返回值,返回最后一个被复制的元素,如:
public static <T> T copy()(Collection<T> dest, Collection<? extends T> src) { T last = null; for(T ele : src) { last = ele; dest.add(ele); } }
表面上看起来,上面的方法实现了这个功能,实际上有一个问题:当遍历src集合的元素时,src元素的类型是不确定的(只可以肯定它是T的子类),程序只能用T来笼统的表示各种src集合的元素类型。如:
List<Number> ln = new ArrayList<>();List<Integer> li = new ArrayList<>();//下面代码引起编译错误Integer last = copy(ln, li);
ln的类型是List<Number>,可得到T的实际类型是Number,而不是Integer,即copy()方法的返回值也是Number类型,而不是Integer类型,但实际上最后一个复制元素的元素类型一定是Integer。也就是说,程序在复制集合元素的过程中,丢失了src集合元素的类型。
对于上面的copy方法,可以这样理解两个集合参数之间的依赖关系:不管src集合元素的类型是什么,只要dest集合元素的类型与前者相同或是前者的父类即可。为了表达这种约束关系,Java允许设定通配符的下限:<? super Type>,这个通配符表示它必须是Type本身,或是Type的父类。如:
import java.util.ArrayList;import java.util.Collection;import java.util.List;public class MyUtils { public static void main(String[] args) { List<Number> ln = new ArrayList<>(); List<Integer> li = new ArrayList<>(); li.add(5); Integer last = copy(ln, li); System.out.println(last); } //下面dest集合元素的类型必须与src集合元素的类型相同,或是其父类 public static <T> T copy(Collection<? super T> dest, Collection<T> src) { T last = null; for(T ele : src) { last = ele; dest.add(ele); } return last; }}
Java集合框架中的TreeSet<E>有一个构造器用到了设定通配符下限的语法:
TreeSet(Comparator<? super E> c)
Compartor接口是一个带泛型声明的接口:
public interface Comparator<T> { int compare(T fst, T snd);}
import java.util.Comparator;import java.util.TreeSet;public class TreeSetTest { public static void main(String[] args) { //Comparator的实际类型是TreeSet的元素类型的父类,满足要求 TreeSet<String> ts1 = new TreeSet<>( new Comparator<Object>() { public int compare(Object fst, Object snd) { return hashCode() > snd.hashCode() ? 1 : hashCode() < snd.hashCode() ? -1 :0; } }); ts1.add("hello"); ts1.add("wa"); TreeSet<String> ts2 = new TreeSet<>( new Comparator<String>() { public int compare(String first, String second) { return first.length() > second.length() ? -1 : first.length() < second.length() ? 1 : 0; } }); ts2.add("hello"); ts2.add("wa"); System.out.println(ts1); System.out.println(ts2); }}
6、泛型与数组
Java泛型有一个很重要的设计原则--如果一段代码在编译时没有提出“【unchecked】未经检查的转换”警告,则程序在运行时不会引发ClassCastException异常。正是基于这个原因,所以数组元素的类型不能包含类型变量或类型形参,除非是无上限的类型通配符。但可以声明元素类型包含类型变量或类型形参的数组。也就是说,只能声明List<String>[]形式的数组,但不能创建ArrayList<String>[10]这样的数组对象。
List<String>[] lsa = new ArrayList<String>[10]; //这是不允许的//Java允许创建无上限的通配符泛型数组,因此下面这行代码是正确的List<?>[] lsa = new ArrayList<?>[10];Object[] oa = (Object[])lsa;List<Integer> li2 = new ArrayList<Integer>();li.add(new Integer(3));oa[1] = li;Object target = lsa[1].get(0);if(target instanceof String) { //下面代码安全了 String s = (String )target;}