3-类型通配符
(1)如果Foo是Bar的子类型(或者子接口),G是具有泛型声明的类或者接口,G<Foo>并不是G<Bar>的子类型;
(2)假设有Foo是Bar的子类型,则Foo[]依然是Bar[]的子类型。
1.类型通配符的使用
为了表示各种泛型的List的父类,可以使用类型通配符,类型通配符是一个问号(?),将这个“?”作为参数传递给List集合,表示它的元素可以匹配任何类型(同样,类型通配符也适用于Set、Map、Collection或其他类型和接口):
import java.util.ArrayList; import java.util.List; public class Test2 { public static void main(String[] args) { List<?> list = new ArrayList<>(); //因为程序复发确定list集合中的元素类型,所以无法向其添加元素 //下面代码错误 //list.add(1); //但是可以通过get()方法来获取list集合指定索引处的元素,返回值类型为Object list.get(0); } }
2.设定类型通配符的上限
(1)适用情形:如一个泛型List<?>是某一类泛型List的父类而不是全部泛型List的父类,可以通过设定类型通配符上限来实现;
(2)例子:
简单的绘图程序,下面先定义三个形状类:
Shape:抽象父类
//定义一个抽象类Shape public abstract class Shape { public abstract void draw(Canvas c); }
Circle:Shape的子类
public class Cricle extends Shape{ //用打印字符串模拟画图方法的实现 @Override public void draw(Canvas c) { // TODO Auto-generated method stub System.out.println("在画布"+c+"上画一个圆"); } }
Rectangle:Shape的子类
public class Rectangle extends Shape{ //用打印字符串的方式模拟画图方法的实现 @Override public void draw(Canvas c) { // TODO Auto-generated method stub System.out.println("在画布"+c+"上画一个矩形"); } }
Canvas:画布类,可以画数量不等的形状(Shape的子类)
import java.util.ArrayList; import java.util.List; public class Canvas { /* //方式1:同时在画布上绘制多个形状 public void drawAll(List<Shape> shapes){ for(Shape s:shapes){ s.draw(this); } } */ /* //方式2: public void drawAll(List<?> shapes){ for(Object obj : shapes){ Shape s = (Shape)obj; s.draw(this); } } */ //方式3 //<? extends Shapes>表示所有Shape泛型List的父类,使List被限制 public void drawAll(List<? extends Shape> shapes ){ for(Shape s : shapes){ s.draw(this); } } public static void main(String[] args) { /* //当使用方式1时 List<Cricle> cricles = new ArrayList<>(); Canvas c = new Canvas(); //下面代码报错 //因为List<Cricle>不是List<Shape>的子类型 c.drawAll(cricles); */ /* //当使用方式2时,虽然代码没有问题,但是在实现过程中使用了泛型仍然需要进行类型转换,特别繁琐 List<Cricle> cricles = new ArrayList<>(); Canvas c = new Canvas(); //下面代码报错 //因为List<Cricle>不是List<Shape>的子类型 c.drawAll(cricles); */ List<Cricle> cricles = new ArrayList<>(); Canvas c = new Canvas(); //下面代码报错 //因为List<Cricle>不是List<Shape>的子类型 c.drawAll(cricles); } }
3.设定类型形参的上限
(1)类型形参的上限表示传给该类型形参的实际类型要么是该上限类型,要么是该上限类型的子类;
(2)类型形参上限的简单使用:
public class Student<T extends Number> { T hight; public static void main(String[] args) { Student<Integer> stu1 = new Student<>(); Student<Double> stu2 = new Student<>(); //因为String不是Number类的子类,所以下面代码编译错误 //Student<String> stu3 = new Student<>(); } }
(3)当程序需要为类型形参设定多个上限时(至多有一个父类上限,可以有多个接口上限),表明该类型形参必须是其父类的子类(或父类本身),并且实现多个上限接口:
import java.io.Serializable; import java.util.Collection; public class Test3 <T extends Number & Collection<?> & Serializable >{ //.... }