为了让编译器可以准确地判断出泛型方法中类型形参的类型,不要制造迷惑,一旦系统迷惑,就是你错了!如下:


1 import java.util.Collection;
2 import java.util.List;
3 import java.util.ArrayList;
4
5 public class ErrorTest{
6
//声明一个泛型方法,该泛型方法中带一个T类型形参
7
static <T> void test(Collection<T> from, Collection<T> to){
8
for(T ele : from){
9
to.add(ele);
10
}
11
}
12
13
public static void main(String[] args){
14
List<Object> ao = new ArrayList<>();
15
List<String> as = new ArrayList<>();
16
//下面代码将产生编译错误
17
test(as, ao);
18
}
19 }
View Code

上面程序调用test()方法传入两个实际参数,as是List<String>,ao是List<Object>,与test(Collection<T> a, Collection<T> c)相比,编译器无法正确识别T所代表的实际类型。
虽然String是Object的子类,但是集合中没有使用类型形参的上限,所以T只能代表String或Object类中的一个,不能代表两个。
改写为如下程序:


1 import java.util.Collection;
2 import java.util.List;
3 import java.util.ArrayList;
4
5 public class ErrorTest{
6
//声明一个泛型方法,该泛型方法中带一个T类型形参
7
static <T> void test(Collection<? extends T> from, Collection<T> to){
8
for(T ele : from){
9
to.add(ele);
10
}
11
}
12
13
public static void main(String[] args){
14
List<Object> ao = new ArrayList<>();
15
List<String> as = new ArrayList<>();
16
//下面代码将产生编译错误
17
test(as, ao);
18
}
19 }
View Code
程序中test(Collection<? extends T> from, Collection<T> to):只要前一个Collection集合里的元素类型是后一个Collection集合里元素类型的子类即可。
那么什么时候使用泛型方法?什么时候使用类型通配符?
泛型方法和类型通配符的区别:
大多数时候都可以使用泛型方法来代替类型通配符。如,Java的Collection接口中的两个方法:


1 public interface Collection<E>{
2
boolean containsAll(Collection<?> c);
3
boolean addAll(Collection<? extends E> c);
4 }
View Code
上面集合中两个方法的形参都采用了类型通配符的形式,也可以采用泛型方法的形式:


1 public interface Collection<E>{
2
<T> boolean containsAll(Collection<T> c);
3
<T extends E>boolean addAll(Collection<T> c);
4 }
View Code
若方法中一个形参(a)的类型或返回值类型依赖于另一个形参(b)的类型,则形参(b)的类型声明不应该是通配符,如:
public static <T> void copy(List<T> dest, List<? extends T> src) { ... }//这里dest形参的类型不能是通配符?,否则两个形参类型模糊。
上面的代码可以改为如下:
public static <T, S extends T> void copy(List<T> dest, List<S> src) { ... }//正是因为这里的S类型形参只使用了一次,所以没有存在的必要,直接用类型通配符代替即可
Java7的“菱形”语法与泛型构造器:
泛型构造器:


1 class Foo{
2
public <T> Foo(T t){
3
System.out.println(t);
4
}
5 }
6
7 public class GenericConstructor{
8
public static void main(String[] args){
9
//泛型构造器中的T参数为String
10
new Foo("疯狂Java讲义");
11
//泛型构造器中的T参数为Integer
12
new Foo(200);
13
//显示指定泛型构造器中的T参数为String
14
//传给Foo构造器的实参也是String对象,完全正确
15
new <String> Foo("疯狂Android讲义");
16
//显示指定泛型构造器的T参数为String
17
//但传给Foo构造器的实参是Double独享,下面代码是错误的
18
new <String> Foo(12.3);
19
}
20 }
View Code
