泛型(4)-泛型通配符的上限


  当直接使用List<?>这种形式时,即表明这个List集合可以是任何泛型List的父类,但还有一种特殊的情况,程序不希望这个List<?>是任何泛型List的父类,只希望它代表某一类泛型List的父类,考虑到一个简单的绘图程序.
示例代码.
package com.j1803.Type_wildcards;

public abstract class Shape {
public abstract void showInfo();
public abstract void draw(Canvas canvas);
}

class Circle extends Shape{
@Override
public void draw(Canvas canvas) {
System.out.println("..............正在画圆形................");

}

@Override
public void showInfo() {
System.out.println("..............正在画圆形");
}
}
class Rectangle extends Shape{
@Override
public void draw(Canvas canvas) {
System.out.println("...............正在画矩形........................");

}

@Override
public void showInfo() {
System.out.println("...............正在画矩形");
}
}
class Triangle extends Shape{
@Override
public void draw(Canvas canvas) {
System.out.println("...............正在画三角形..........................");


}

@Override
public void showInfo() {
System.out.println("...............正在画三角形");
}
}


package com.j1803.Type_wildcards;

import java.util.ArrayList;
import java.util.List;

public class Canvas {
//遍历集合中的所有元素
public void shoInfo(List<?> list){
System.out.println("===");
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
if(list.get(i).getClass().equals(new Circle().getClass())){
System.out.println(list.get(i).getClass());
Circle circle=(Circle)list.get(i);
circle.showInfo();
}
}
}

//同时在画布上绘制多个形状
public void drawAll(List<Shape>list){
System.out.println("List<Shape>list");
for(Shape shape:list){
shape.draw(this);
}
}

//同时在画布上绘制多个形状1
public void drawAll1(List<? extends Shape> list){
System.out.println("List<? extends Shape>");
for(Shape shape:list){
shape.draw(this);
}

}
public static void main(String[] args) {
Canvas canvas=new Canvas();
List<Shape>list=new ArrayList<>();
list.add(new Circle());
list.add(new Rectangle());
list.add(new Triangle());
canvas.shoInfo(list);

System.out.println("********************************************************");
List<Circle>circles=new ArrayList<>();
circles.add(new Circle());
//circles.add(new Triangle());//编译错误
canvas.shoInfo(circles);

System.out.println("********************************************************");
canvas.drawAll(list);

System.out.println("********************************************************");
canvas.drawAll1(circles);



}
}
关键在于List<Cirlce>并不是List<Shape>的子类,所以不能把List<Circle>对象当成List<Shape>使用,为了表示List<Circle>的父类,可以考虑使用List<?>,但同时从List<?>集合取出的元素只能被编译器当成Object处理,
为了表示List集合的所有元素是Shape的子类,Java泛型提供了被限制的泛型通配符,被限制的泛型通配符表示如下:
List<? extends Shape>//它表示泛型形参必须是Shape子类的List.
List<Circle>对象当成List<? extends Shape>使用.即List<? extends Shape>可以表示List<Circle>,List<Rectangle>的父类,只要List后尖括号里面的类型是Shape的子类就可以了.
List<? extends Shape>是受到限制的通配符的例子,此处的问号(?)代表一个未知的类型,就像前面看到的通配符一样,但是此处的这个未知类型一定是Shape的子类型(也可以是Shape本身),因此可以把Shape称为这个通配符的上限.
类似的,由于程序无法确定这个受限的通配符的具体类型,所以不能把Shape对象或者其子类的对象加入这个泛型集合中,
public class Demo2 {

public void add(list<? extends Shape> shape){
shape.add(0,new Rectangle());
}

与使用普通方法相似的是,shape.add()的第二个参数类型是? extends Shape,它表示Shape未知的子类,程序无法确定这个类型是什么,所以无法将任何对象添加到这种集合中.
简而言之,这种指定通配符上限的集合,只能从集合中取出元素(取出的元素总是上限的类型),不能向集合中添加元素(因为编译器无法确定集合元素实际是哪一种子类型).
对于更广泛的泛型类来说,指定通配符上限,局势为了支持类型新编,比如Foo是Bar类型的子类,这样A<Bar>就相当于A<? extends Foo>的子类,可以把A<Bar>赋值给A<? extends Foo>类型的变量,这种型变方式称为协变
对于协变的泛型类来说,它只能调用泛型类型作为返回值类型的方法(编辑器会将该方法返回值当成通配符上限的类型);而不能调用泛型类型作为参数的方法,口诀是:协变只进不出.
对于指定了通配符上限的泛型类,相当于通配符上限是Object




猜你喜欢

转载自www.cnblogs.com/shadow-shine/p/9656536.html