您的位置:首页 > 编程语言 > Java开发

java泛型通配符

2016-07-09 14:36 253 查看
考虑到常规打印集合所有元素的问题,然而你将写一个比较好的版本(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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: