Java泛型之通配符类型
1. 案例分析
class Animal {
@Override
public String toString() {
return "animal";
}
}
class Cat extends Animal {
@Override
public String toString() {
return "cat";
}
}
class Dog extends Animal {
@Override
public String toString() {
return "dog";
}
}
public class App {
public static void show(ArrayList<Animal> animals) {
for(Animal a : animals) {
System.out.println(a);
}
}
public static void main(String[] args) {
ArrayList<Cat> list = new ArrayList<Cat>();
list.add(new Cat());
show(list);
list.get(0).getCat();
}
}
在main方法中, 构造了一个ArrayList类的对象, 调用show方法打印该对象中的元素。 一切看起来都很正常, 结果在编译时, 编译器报错如下:
[分析]:
Cat是Animal的子类, ArrayList<Cat>
理应是ArrayList<Animal>
的子类了, 为何还提示“不兼容的类型”? 其实这只不过是我们想当然而已,假如ArrayList<Cat>
可以转型为ArrayList<Animal>
, 那么利用后者就可以添加任何派生于Animal的对象了, 如Dog类的对象, 这就失去了ArrayList<Cat>
原本的目的了, 因此编译器才会给我们报告错误。
那么ArrayList<Cat>
、 ArrayList<Dog>
, 甚至ArrayList<String>
, 它们有没有公共的父类型呢? 答案是有, 那就是ArrayList<?>
, "?"
是通配符, 代表任意类型。
好,修改代码,将show()中的参数修改为ArrayList<?>类型。
同时注意,在show()中,由于元素类型是未知的,所以不能用for(Animal a : animals)来循环,但是所有的对象类型都是Object的子类,所以可以使用Object来替代
,否则报错(不兼容的类型, capture#1, 共 ?无法转换为Animal)。
public static void show(ArrayList<?> animals) {
for(Object a : animals) {
System.out.println(a);
}
}
此时编译,一切正常运行。
[注意]:
再来关注一下 void show(ArrayList<?> animals)
函数,由于使用了通配符类型, 因此不能往animals中写入任何对象, 毕竟元素的类型是未知的, 如果允许你写入, 那岂不是可以写入任意类型的对象了? 显然, 这是不允许的
。 同时也不能调用animals中存储的对象的方法(从Object类继承的方法除外) , 因为类型都不确定, 又怎么能知道这些方法有没有呢? 例如下面的代码在编译期间就会报错。
public static void show(ArrayList<?> animals) {
animals.add(new Dog()); // error
animals.get(0).showCat(); // error
// ...
}
2. 通配符的子类型限定(<? extends T>)
"?"
这种通配符貌似功能很强大, 可以匹配任何类型, 但实际上,你会发现什么都做不了。 其实, 我们可以对通配符做限定, 来缩小通配符匹配的范围
。 例如, 对于上面的例子, 我们希望是任何Animal类的子类型, 而不是所有类型
, 于是可以用通配符: ? extends Animal
来表示。 要注意的是, 这里的extends和类继承的extends并不是一个含义, 前者只是表示Animal或者任何Animal的子类型
, extends后的类型也可以是接口类型。
对通配符进行限定后就比较明确了, ArrayList<? extends Animal>
是ArrayList<Cat>
和ArrayList<Dog>
的父类型, 而不是ArrayList<String>
或ArrayList<Double>...
的父类型。
abstract class Shape {
public abstract void draw(Canvas c);
}
class Circle extends Shape {
private int num;
public Circle(int num) {
this.num = num;
}
@Override
public void draw(Canvas c) {
System.out.println("draw circle " + this.num + ".");
}
}
class Rectangle extends Shape {
private int num;
public Rectangle(int num) {
this.num = num;
}
@Override
public void draw(Canvas c) {
System.out.println("draw rectangle " + this.num + ".");
}
}
class Canvas {
public void drawAll(ArrayList<? extends Shape> list) {
for(Shape shape : list) {
shape.draw(this);
}
}
}
public class App {
public static void main(String[] args) {
ArrayList<Circle> circleList = new ArrayList<Circle>();
circleList.add(new Circle(111));
circleList.add(new Circle(222));
ArrayList<Rectangle> rectList = new ArrayList<Rectangle>();
rectList.add(new Rectangle(333));
rectList.add(new Rectangle(444));
Canvas can = new Canvas();
can.drawAll(circleList);
can.drawAll(rectList);
}
}
3. 通配符的超类型限定(<? super T>
)
未完待续。。。