通配符
考虑一个编程问题:打印出一个集合的所有的元素,
方法一:
void printCollection(Collection c) {
Iterator i = c.iterator();
for (k = 0; k < c.size(); k++) {
System.out.println(i.next());
}
}
方法二:
void printCollection(Collection<Object> c) {
for (Object e : c) {
System.out.println(e);
}
}
现在问题来了!方法二的使用范围比方法一的适用范围要小,为什么呢?因为方法一它的参数是Collection c 呀,它可以接受任意类型的Collection.而方法二,它只能接受Collection<Object>的集合,Collection<Object> is not a supertype of all kinds of collections!
那我们有没有一个接受任意类型的超级泛型集合呢?
是的,就是Collection<?>, 它可以和任意类型的泛型集合相匹配。如下是我们改进下的方法二:
void printCollection(Collection<?> c) {
for (Object e : c) {
System.out.println(e);
}
}
现在,我们就可以使用这个方法二,去接受Collection<String>或者Collection<Integer>等等集合了。但这里还要注意的是:方法二代码,从集合c 里面取出元素,赋值给Object e,这是安全的,因为任何类型的数据都是Object类型的子类。另外,下面的操作是不可取的:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
因为我们不知道集合C里面是存的哪种类型,C可能是 Collection<String>或者Collection<Integer>,所以不能向里面添加任何数据(除了null,null代表任意类型)。
有边界的通配符
1.考虑一个简单画图程序,它能画方形,圆形等。代码如下:
public abstract class Shape {
public abstract void draw(Canvas c);
}
public class Circle extends Shape {
private int x, y, radius;
public void draw(Canvas c) {
...
}
}
public class Rectangle extends Shape {
private int x, y, width, height;
public void draw(Canvas c) {
...
}
}
/**
These classes can be drawn on a canvas:
*/
public class Canvas {
public void draw(Shape s) {
s.draw(this);
}
public void drawAll(List<Shape> shapes) {
for (Shape s: shapes) {
s.draw(this);
}
}
让他接受任何类型的shape集合(包括ArrayList<Rectangle>,ArrayList<Circle>等等),但现在呢,它是不能像这样调用的,如下:
List<Circle> list = new ArrayList<Circle>();
drawAll(list)//编译错误。理由前面说过了。
现在,我们改进drawAll方法,如下:
public void drawAll(List<? extends Shape> shapes) {
...
}
那么这个时候我们的方法就实现了我们上述的目的了,List<? extends Shape> 是可以接受shape本身,或者shape任意子类的List集合的。
未知 ,在这个列子中,?可以代表shape的子类(如Rectangle,Circle),或者shape本身。我们称这样的为“上边界的集合”。
2.再看下面代码,如下:
public void addRectangle(List<? extends Shape> shapes) {
// Compile-time error!
shapes.add(0, new Rectangle());
}
上面操作不被允许的,
我们知道shapes形参集合可能是Collection<Rectangle>或者Collection<Circle>,是不确定集合
假设shapes是Collection<Circle>,那上面的操作明显是错的嘛,因为Circle集合里面不能存Rectangle的。
但这个集合是可以“取”的,就是说 Shape shape = shapes.get(0) 是被认可的,理由简单:shapes存的肯定都是shape或shape的子类,那这种赋值操作当然没问题的了。
总而言之,一个集合你不确定它是哪种类型的集合,你就不能“冒失”的向里面添加东西。
3,考虑这个集合List<? super Rectangle>
这个集合是叫做“下边界的集合” ,它是接受以Rectangle本身,或者Rectangle父类为泛型的集合,是不确定集合。注意如下代码:
List<? super Rectangle> c = new ArrayList<Shape>();
c.add(new Rectangle());//这是编译通过的。
为什么它能添加!!!,不是说不能确定c是哪种集合吗?
对,但也不对,
对的地方在于 集合C 可能是ArrayList<Rectangle> 或者 ArrayList<Shape>,这是不确定的;
不对的地方是,有一点是可以确定的,就是集合C 存的东西 都是 Rectangle 的父类 或者 Rectangle本身。考虑如下代码:
List<Ractangle> list = new ArrayList<Ractangle>();
list.add(new Ractangle());//这是可行的。
List<Shape> list = new ArrayList<Shape>();
list.add(new Ractangle());//这是也是可行的。
能存 Rectangle实例,或者Rectangle子类。
但是不能“直接取直接赋值", 因为 从集合C取的东西,谁知道 是Rectangle实例,还是 Object实例。