边界
在泛型的参数类型上设置的限制条件。例如:使用extends关键字<T extends AClass>
2、边界可以有多个
边界可以是类也可以是接口,接口可以有多个而类只可以有一个。类放在前,接口在后
例如:
class A{}
Interface I1{}
Interface I2{}
public class B<T extends A & I1 &I2>{}
通配符
通配符表示表示任何一个类型,用“?”代表通配符。
1、子类限定通配符<? extends AClass>,它表示一个类,这个类是AClass的子类。
(1)子类限定通配符可以使你建立某种类型的向上转型关系。例如:
假如Apple是Fruit的子类。
//编译错误,这里并没有向上转型的关系。List<Apple>不是List<Fruit>的子类
List<Fruit> list = new ArrayList<Apple>();
使用子类限定通配符可以建立向上转型的关系。但是无法使用参数是泛型参数的方法。
List<? extends Fruit> list = new ArrayList<Apple>();//编译通过
list.add(new Fruit());//编译错误
list.add(new Apple());//编译错误
list.add(new Object());//编译错误
list.add(null);//可以
Fruit f = list.get(0);//可以
list.contains(new Apple());//可以
但是如果像上面这样声明一个List,它无法添加任何对象(除了null),但是可以使用get()和contains(),方法。可以这么理解:
对于不能添加对象:
add的参数是T,也就是这个参数将变成“? extends Fruit”,这代表这它可以是任何事物,编译器无法确认任何事物的类型安全。
对于可以使用的get()等方法:
通过源码可以发现,这些方法的参数是Object,所以无论什么类型的对象都可以。
2、超类型通配符<? super AClass>
可以向List<? super AClass>中添加AClass和它的子类。
返回值为泛型的方法不能使用。
3、无界通配符
(1)无界通配符代表某个具体的类型,而原生类型代表任何类型。
原生类型什么都能放。
List l1 = new ArrayList<>();
l1.add("aa");
l1.add(1);
Object o = l1.get(0);
使用无界通配符后,什么都不能放(除了null)。可以使用get()取,因为还不知道确切的类型,所以只能用Object。
List<?> l = new ArrayList<>();
l.add(1);//编译错误
Object o = l.get(0);
(2)捕获转换
假如方法f1()的参数为List<?>,泛型方法f2()的参数为List<T>,那么就可以在f1()中调用f2()
public class Test{
/*
*调用f1()时传给它的参数的具体类型将被捕获,也就是说当调用之后才知道他的具体类型
*所以可以将它传给f2
*/
public static void f1(List<?> list){
f2(list);
}
public static <T> void f2(List<T> list){
T t = list.get();
}
public static void main(String[] args){
List<String> list = new ArrayList<>();
f1(list);
}
}
这节学得很迷,书上的语句实在难理解。