java泛型-通配符


通配符

  考虑一个编程问题:打印出一个集合的所有的元素,

  方法一:

void printCollection(Collection c) {
     Iterator i = c.iterator();
     for (k = 0; k < c.size(); k++) {
        System.out.println(i.next());
    }
}  

  方法二:

void printCollection(Collection c) {
    for (Object e : c) {
        System.out.println(e);
    }
}  

  现在问题来了!方法二的使用范围比方法一的适用范围要小,为什么呢?因为方法一它的参数是Collection c 呀,它可以接受任意类型的Collection.而方法二,它只能接受Collection的集合,Collection is not a supertype of all kinds of collections!

那我们有没有一个接受任意类型的超级泛型集合呢?

  是的,就是Collection<?>, 它可以和任意类型的泛型集合相匹配。如下是我们改进下的方法二:

void printCollection(Collection<?> c) {
    for (Object e : c) {
        System.out.println(e);
    }
}    

  现在,我们就可以使用这个方法二,去接受Collection或者Collection等等集合了。但这里还要注意的是:方法二代码,从集合c 里面取出元素,赋值给Object e,这是安全的,因为任何类型的数据都是Object类型的子类。另外,下面的操作是不可取的:

  Collection<?> c = new ArrayList();
  c.add(new Object()); // Compile time error

  因为我们不知道集合C里面是存的哪种类型,C可能是 Collection或者Collection,所以不能向里面添加任何数据(除了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 shapes) {

        for (Shape s: shapes) {

        s.draw(this);
   }
  }

  注意到Canvas类中的drawAll方法,我们书写这个方法的目的是:让他接受任何类型的shape集合(包括ArrayList,ArrayList等等),但现在呢,它是不能像这样调用的,如下:

  List list = new ArrayList();

  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或者Collection
是不确定集合
    假设shapes是Collection,那上面的操作明显是错的嘛,因为Circle集合里面不能存Rectangle的。
  
但这个集合是可以“取”的,就是说 Shape shape = shapes.get(0) 是被认可的,理由简单:shapes存的肯定都是shape或shape的子类,那这种赋值操作当然没问题的了。
  
  总而言之,一个集合你不确定它是哪种类型的集合,你就不能“冒失”的向里面添加东西。

 3,考虑这个集合List<? super Rectangle>
   
   这个集合是叫做“
下边界的集合” ,它是接受以Rectangle本身,或者Rectangle父类为泛型的集合,是不确定集合。注意如下代码:
    List<? super Rectangle> c = new ArrayList();
    c.add(new Rectangle());//这是编译通过的。

    为什么它能添加!!!,不是说不能确定c是哪种集合吗?

    对,但也不对,

           对的地方在于 集合C 可能是ArrayList 或者 ArrayList,这是不确定的;

           不对的地方是,有一点是可以确定的,就是集合C 存的东西 都是 Rectangle 的父类 或者 Rectangle本身。考虑如下代码:

    List list = new ArrayList();
    list.add(new Ractangle());//这是可行的。


    List list = new ArrayList();
    list.add(new Ractangle());//这是也是可行的。

    如此,我们总可以向集合C 存 Rectangle实例,存 Rectangle实例,或者Rectangle子类。

    但是不能“直接取直接赋值 Rectangle r = c.get(0) ", 因为 从集合C取的东西,谁知道 是Rectangle实例,还是 Object实例。