java泛型通配符


考虑到常规打印集合所有元素的问题,然而你将写一个比较好的版本(jdk1.5以前)

void printCollection(Collection c) {
    Iterator i = c.iterator();
    for (k = 0; k < c.size(); k++) {
        System.out.println(i.next());
    }
}
然而有一个幼稚的想法去使用泛型(使用新的for)
void printCollection(Collection<Object> c) {
    for (Object e : c) {
        System.out.println(e);
    }
}
问题是,这种新的版本的使用比旧版本使用少得多。其中,作为旧代码可以与任何种类的集合作为参数调用,而新的代码只能使用Collection<Object>,其中,因为我们刚刚的演示,不是所有类型的集合的超类型

哪什么才是任何集合类型的超类型?它应该被写成Collection<?>(表示集合类型未知), 这样可以匹配任何类型. 
这就是所谓的通配符类型,原因很明显。我们可以这样写
void printCollection(Collection<?> c) {
    for (Object e : c) {
        System.out.println(e);
    }
}
然而,现在我们调用任何集合类型。注意printCollection()的内部我们可以一直读取元素并赋给Object类型,这是安全的。因为任何集合的实际类型,它包含对象。它不安全的是不能任意添加元素。
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
c.add(null);//OK  because null is any type

由于我们不知道c代表的是什么元素,我们不能向集合添加对象。add()方法里面的参数为E,也是集合类型,当使用?作为参数是,它代表的类型是未知的,我们通过添加任何参数必须是这个未知类型的子类型。因为我们不知道那是什么类型的,我们不能传递任何东西。唯一的例外是null,它是每一个类型的成员。

另一方面,获得一个List<?>,我们可以通过调用get()方法获取和使用元素,这个元素的类型是未知的,但是我们总知道它是一个对象,将get()的结果赋值给Object类型的变量时安全的把它作为其中类型对象期望的参数。

边界通配符
考虑一个简单的画图一样,能够花的图形例如矩形和圆形。类的继承结构如下:
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) {
        ...
    }
}

这些类都能在画布上画图:
public class Canvas {
    public void draw(Shape s) {
        s.draw(this);
   }
}


任何绘图通常含有多种形状的。假设他们表示为一个列表,这将是方便的在Canvas的方法,吸引了所有这些:
public void drawAll(List<Shape> shapes) {
    for (Shape s: shapes) {
        s.draw(this);
   }
}


现在,类型规则说drawAll()只能算得上准确形状的名单:它不能,例如,算得上一个List <Circle>。这是不幸的,因为所有的方法不会被从列表中读出的形状,所以它也可以同样可以在一个List <Circle>调用。我们真正需要的是接受任何一种形状的列表的方法:
public void drawAll(List<? extends Shape> shapes) {
    ...
}


这是聪明的,但是非常重要的地方:我们是用List<? extends Shape>替换了List<Shape>。现在drawAll() 可以接受Shape的子类类型,如果我们想使用,我们现在就可以使用List<Circle>调用。
List<? extends Shape> 是一个边界通配的示例。而这个?代表的未知类型,就像以前看到的通配符。然而,例如,我们虽然知道那个未知类型是Shape的子类型。(Note:它可能是自身或它的子类,不需要字面继承Shape)我们说Shape是通配符的上界。
还有就是,像往常一样,价格要支付使用通配符的灵活性。价格是它现在是非法写入形状的方法。例如,这是不允许的:
public void addRectangle(List<? extends Shape> shapes) {
    // Compile-time error!
    shapes.add(0, new Rectangle());
}


你应该能够找出为什么上面的代码是不允许的。shapes.add()的第二个参数是什么?继承Shape--一个Shape的未知类型。因为我们不知道它的类型是什么,如果它是一个Rectangle的父类型,它可能或不可能是一个父类型,因此它添加Rectangle是不安全的。

原文链接:https://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值