你觉得泛型协变很简单?很容易明白?那么我们来看看下面的代码片段。在继续本文之前,请务必读一下这篇文章:Java中的泛型
首先,我们来看看这个。
示例一:
import java.util.ArrayList;
import java.util.List;
public class Demo1 {
public static void doNothing(List<?> list1, List<?> list2) {}
public static void main(String[] args) {
List<String> list1 = new ArrayList<String>();
List<Integer> list2 = new ArrayList<Integer>();
doNothing(list1, list2);
}
}
这个编译通过,没有问题。
那么看看这个。
示例二:
import java.util.ArrayList;
import java.util.List;
public class Demo1 {
public static void doNothing(List<?> list1, List<?> list2) {
list1.add(list2);
// Error: The method add(capture#1-of ?) in the type List<capture#1-of ?>
// is not applicable for the arguments (List<capture#2-of ?>)
}
public static void main(String[] args) {
List<String> list1 = new ArrayList<String>();
List<Integer> list2 = new ArrayList<Integer>();
doNothing(list1, list2);
}
}
这里就编译不通过了,为啥?不是有通配符吗?通配符不是可以代表一切类型吗?
好吧,我这么改下。把前面的那个List<?>改成List<List<?>>不就可以了。里面的那个嵌套不就正好是后面的那个格式吗?
示例三:
import java.util.List;
public class Demo1 {
public static void doNothing(List<List<?>> list1, List<?> list2) {
list1.add(list2);
}
}
果然,这次编译通过了。
list1的嵌套元素类型和list2的类型都未确定的前提下,其形式表达上是一致的,这么写可以编译通过。
那我再变化一下。
示例四:
import java.util.List;
public class Demo1 {
public static void doNothing(List<List<? extends Number>> list1, List<?> list2) {
list1.add(list2);
// Error: The method add(List<? extends Number>) in the type List<List<? extends Number>>
// is not applicable for the arguments (List<capture#1-of ?>)
}
}
这里又出问题了。前面的list1可以接受一个元素类型限定在Number子类的List类型,但是后面的list2明明是有通配符的啊!通配符不是可以适应任何类型吗?
难道是list1和list2的类型不匹配?
那再这么改。
示例五:
import java.util.List;
public class Demo1 {
public static void doNothing(List<List<? extends Number>> list1, List<? extends Number> list2) {
list1.add(list2);
}
}
这次编译通过了。
list1的嵌套元素类型和list2的类型,在形式上都是一致的。这么写可以编译通过。
那我把示例三的main函数加上看看。
示例六:
import java.util.List;
public class Demo1 {
public static void doNothing(List<List<?>> list1, List<?> list2) {
list1.add(list2);
}
public static void main(String[] args) {
List<List<String>> list1 = null;
List<String> list2 = null;
doNothing(list1, list2);
// Error: The method doNothing(List<List<?>>, List<?>)
// in the type Demo1 is not applicable for the arguments (List<List<String>>, List<String>)
}
}
这次又编译不通过了。
为啥?没加main方法的时候不是编译通过的吗?list1的类型确定后是List<List<String>>,而list2的类型确定后是List<String>,list2的类型不正好是list1的嵌套类型吗?为啥编译不通过?
其实,你把上面的代码片段都加上main方法,没有一个能编译通过。你木有看错,它们全是错的!怎样?头晕了吧!
警世恒言:别被JVM骗了!某些时候你明明声明的泛型方法没有问题,但是当你用它们的时候,却发现用不了,而且很难发现错在哪里!有木有!
import java.util.ArrayList;
import java.util.List;
public class Demo1 {
public static void doNothing(List<List<? extends Number>> list1, List<? extends Number> list2) {
list1.add(list2);
}
public static void doSomething() {
}
public static void main(String[] args) {
List<List<? extends Number>> list1 = new ArrayList<List<? extends Number>>();
List<? extends Number> list2 = new ArrayList<Number>();
doNothing(list1, list2);
}
}
将示例三,进行类型限定之后,这个代码是可以编译通过的。关于多通配符的泛型方法声明,本篇只讨论了其中一种形式。这里面槽点太多,大家慢慢回味吧。
把上面的示例作如下改动:
import java.util.ArrayList;
import java.util.List;
public class Demo1 {
public static void doNothing(List<List<? extends Number>> list1, List<? extends Number> list2) {
list1.add(list2);
System.out.println("output: " + list1.toString());
}
public static void doSomething() {
}
public static void main(String[] args) {
List<List<? extends Number>> list1 = new ArrayList<List<? extends Number>>();
List<Number> list2 = new ArrayList<Number>();
list2.add(new Integer(1));
list2.add(new Double(2.0));
list2.add(new Float(3.0f));
doNothing(list1, list2);
}
}
输出:
output: [[1, 2.0, 3.0]]
再把上面的示例作一次变化,这次我们不用List.add()方法,换成List.contains()方法。如下:
import java.util.ArrayList;
import java.util.List;
public class Demo1 {
public static void doNothing(List<List<? extends Number>> list1, List<? extends Number> list2) {
list1.add(list2);
System.out.println("output: " + list1.toString());
}
public static void doSomething(List<? extends List<? extends Number>> list1, List<? extends Number> list2) {
boolean b = list1.contains(list2);
System.out.println("contains: " + b);
}
public static void main(String[] args) {
List<List<? extends Number>> list1 = new ArrayList<List<? extends Number>>();
List<Number> list2 = new ArrayList<Number>();
list2.add(new Integer(1));
list2.add(new Double(2.0));
list2.add(new Float(3.0f));
doNothing(list1, list2);
doSomething(list1, list2);
}
}
输出:
output: [[1, 2.0, 3.0]]
contains: true
好吧,因为用List.add()会报错。所以,我换成了List.contains()方法。原因嘛,你懂的。
关键在于,要理解
? extends List<? extends Number>
到底是个啥类型。
再次改装上面的示例,这次我们创建一个另类的泛型化的类Pair<T extends List<? extends Number>, D extends List<? extends String>>代码:
import java.util.List;
public class Pair<T extends List<? extends Number>, D extends List<? extends String>> {
private T t;
private D d;
public Pair() {}
public void setT(T t) {
this.t = t;
}
public T getT() {
return t;
}
public void setD(D d) {
this.d = d;
}
public D getD() {
return d;
}
public String toString() {
return t.toString() + "; " + d.toString();
}
}
上面的示例被改为:
import java.util.ArrayList;
import java.util.List;
public class Demo1 {
public static void doNothing(List<List<? extends Number>> list1, List<? extends Number> list2) {
list1.add(list2);
System.out.println("output: " + list1.toString());
}
public static void doSomething(List<? extends List<? extends Number>> list1, List<? extends Number> list2) {
boolean b = list1.contains(list2);
System.out.println("contains: " + b);
}
public static void main(String[] args) {
List<List<? extends Number>> list1 = new ArrayList<List<? extends Number>>();
List<Number> list2 = new ArrayList<Number>();
list2.add(new Integer(1));
list2.add(new Double(2.0));
list2.add(new Float(3.0f));
doNothing(list1, list2);
doSomething(list1, list2);
Pair<List<? extends Number>, List<? extends String>> p = new Pair<List<? extends Number>, List<? extends String>>();
List<Integer> listA = new ArrayList<Integer>();
listA.add(1);
listA.add(2);
listA.add(3);
List<String> listB = new ArrayList<String>();
listB.add("one");
listB.add("two");
listB.add("three");
p.setT(listA);
p.setD(listB);
System.out.println("Pair to String: " + p.toString());
}
}
其中,Pair的创建那一行还可以写成这样:
Pair<List<Integer>, List<String>> p = new Pair<List<Integer>, List<String>>();
大家仔细回味吧。