定义带泛型的类
public class Cat<T> { //可以用T定义实例变量 private T name; //可以用T定义形参
//构造器没有<> public Cat(T name){ this.name = name; } //可以作为返回值 public T forget(){ System.out.println("我叫啥来着?"); return this.name; } public static void main(String[] args){ Cat<String> cat = new Cat<>("阿猫"); System.out.println(cat.forget()); } }
构造泛型对象可省略类型参数
List<String> list = new ArrayList<>();
//等价于List<String> list = new ArrayList<String>();
//编译器会自己推出后边的类型是String
泛型方法
1.泛型方法可以在普通类或者泛型类
2.类型参数放在修饰符之后,返回值之前
public static <T> void test();
不能在静态变量或者静态方法中使用泛型变量,不能实例化泛型变量
因为泛型是要在对象创建的时候才知道是什么类型的,而对象创建的代码执行先后顺序是static的部分,然后才是构造函数等等。所以在对象初始化之前static的部分已经执行了,如果你在静态部分引用的泛型,那么毫无疑问虚拟机根本不知道是什么东西,因为这个时候类还没有初始化。因此在静态方法、数据域或初始化语句中,为了类而引用泛型类型参数是非法的
public class Entry<k,v>{ //以下2种方式是错误的 private static V value; public static void setValue(V value){}; }
泛型不同类型相同
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
System.out.println(list1.getClass() == list2.getClass());
上面的代码返回值是true,因此list1和list2都是List类型,与泛型不泛型无关。
类型通配符(?)
public void getFamaleCat(List<?> list){ for(int i=0;i<list.size();i++){ System.out.println(list.get(i)); } }
1.当不确定传入的类型时,使用类型通配符?,不要使用List<Object>,当你想要传入一个List<String>作为参数时,程序编译不会通过,而使用?则可以通过
2.当确定类型是某个类的子类型时,使用<? extends Father>,这时传入的类型只能是Father及其子类
public class Util{ /* 方法实现将src中的数据复制到dest中 那么src中的数据类型只能是dest中数据类型的子类型 比如:src中类型是Integer,dest中可以是Integer,Number,Object */ public static <T> void copy(List<T> dest,List<? extends T> src){ for(T d : src){ dest.add(d); } } public static void main(String[] args){ List<Integer> src = new ArrayList<>(); src.add(1); src.add(2); List<Number> dest = new ArrayList<>(); //泛型的匹配方式是直接把参数拿过来和T比 //因为dest的类型是Number,所以推出T是Number,所以dest集合中的类型都是Number copy(dest,src); } }
3.当确定类型时某个类的父类型时,使用<? super son>,这时传入的类型只能是son及son的父类
上面的例子最后dest中的数据类型都是Number,但是我们清楚的知道其实dest中每个数据的类型都是Integer,使用<? super son>改造
public class Util{ public static <T> void copy(List<? super T> dest,List<T> src){ for(T d : src){ dest.add(d); } } public static void main(String[] args){ List<Integer> src = new ArrayList<>(); src.add(1); src.add(2); List<Number> dest = new ArrayList<>(); //dest集合中元素都是Integer类型 copy(dest,src); } }
4.泛型可以重载,不要包括2个意思相同的泛型方法
public static <T> void copy(List<? super T> dest,List<T> src); public static <T> void copy(List<T> dest,List<? extends T> src);
如果我们把这2个方法定义在同一个类里,main调用copy,编译器不能确定到底调用哪个方法
public static void main(String[] args){ List<Integer> src = new ArrayList<>(); src.add(1); src.add(2); List<Number> dest = new ArrayList<>(); copy(dest,src); }
类型擦除
所有的泛型类型会被编译成一个原始类型
这样并不会不安全
例如:List<String>,因为泛型会在编译的时候起作用,实际上你传入集合的所用元素都只能是String类型
public class Cat<Object> { private Object name; public Cat(Object name){ this.name = name; } public Object forget(){ System.out.println("我叫啥来着?"); return this.name; } }