(1) 若是你定义了一个泛型(类、接口),那么Java规定,你不能在全部的静态方法、静态初块等全部静态内容中使用泛型的类型参数。例如:java
public class A<T> { public static void func(T t) { //报错,编译不经过 } }
(2) 如何在静态内容(静态方法)中使用泛型,更通常的问题是,若是类(或者接口)没有定义成泛型,可是就想在其中某几个方法中运用泛型(好比接受一个泛型的参数等),该如何解决?安全
public class A<T> { ... }
代表在类A的做用域中,T是泛型类型参数。public static <T, S> int func(List<T> list, Map<Integer, S> map) { ... }
,其中T和S是泛型类型参数。
(3) 类型参数的做用域code
class A<T> { ... }
中T的做用域就是整个A;public <T> func(...) { ... }
中T的做用域就是方法func;对象
类型参数也存在做用域覆盖的问题,能够在一个泛型模板类/接口中继续定义泛型方法,例如:接口
class A<T> { // A已是一个泛型类,其类型参数是T public static <T> void func(T t) { // 再在其中定义一个泛型方法,该方法的类型参数也是T } } //当上述两个类型参数冲突时,在方法中,方法的T会覆盖类的T,即和普通变量的做用域同样,内部覆盖外部,外部的同名变量是不可见的。 //除非是一些特殊需求,必定要将局部类型参数和外部类型参数区分开来,避免发生没必要要的错误,所以通常正确的定义方式是这样的: class A<T> { public static <S> void func(S s) { } }
(4) 泛型方法的类型参数能够指定上限,类型上限必须在类型参数声明的地方定义上限,不能在方法参数中定义上限。规定了上限就只能在规定范围内指定类型实参,超出这个范围就会直接编译报错。作用域
<T extends X> void func(List<T> list){ ... }
,正确<T extends X> void func(T t){ ... }
,正确<T> void func(List<T extends X> list){ ... }
,编译错误(1) 显式指定方法的类型参数,类型参数要写在尖括号中并放在方法名以前。例如:object.<String> func(...)
,这样就显式指定了泛型方法的类型参数为String,那么全部出现类型参数T的地方都将替换成String类型。编译器
(2) 隐式地自动推断,不指明泛型参数,编译器根据传入的实参类型自动推断类型参数。例如:<T> void func(T t){ ... }
隐式调用object.func("name")
,根据"name"
的类型String推断出类型参数T的类型是Stringio
(3) 避免歧义,例如:<T> void func(T t1, T t2){ ... }
若是这样调用的话object.func("name", 15);
虽然编译不会报错,可是仍然会有很大隐患,T到底应该是String仍是Integer存在歧义;编译
(4) 有些歧义Java是会直接当成编译错误的,即全部和泛型参数有关的歧义,例如:<T> void func(List<T> l1, List<T> l2){...}
若是这样调用的话,object.func(new List<String>(), new List<Integer>());
这里会有歧义,编译器没法知道T到底应该是String仍是Integer,这种歧义会直接报错的,编译没法经过。即泛型方法中,若是类型参数恰好就是泛型参数的类型实参,那么这个类型实参不得有歧义,不然直接编译报错。模板
(1) 你会发现全部能用类型通配符(?)解决的问题都能用泛型方法解决,而且泛型方法能够解决的更好。
void func(List<? extends A> list);
<T extends A> void func(List<T> list);
(2) 两种方法能够达到相同的效果,“?”能够表明范围内任意类型,而T也能够传入范围内的任意类型实参,而且泛型方法更进一步,“?”泛型对象是只读的,而泛型方法里的泛型对象是可修改的,即List<T> list
中的list是可修改的。
(3) 二者最明显的区别
(3) 适用场景
public <T> void func(List<T> list, T t) { list.add(t); }
<T> void func(List<? extends T> list, T t);
即第一个参数依赖第二个参数的类型(第一个参数list的类型参数必须是第二个参数的类型或者其子类)。<T, E extends T> void func(List<T> l1, List<E> l2);
这里E只在形参中出现了一次(类型参数声明不算),而且没有任何其余东西(方法形参、返回值)依赖它,那么就能够把E规约成“?”。规约结果<T> void func(List<T> l1, List<? extends T> l2);
public static <T> void Collections.copy(List<T> dest, List<? extends T> src) { ... }
从src拷贝到dest,那么dest最好是src的类型或者其父类,由于这样才能类型兼容,而且src只是读取,不必作修改,所以使用“?”还能够强制避免对src作没必要要的修改,增长的安全性。