1.边界
边界,如果使用泛型没有设置其边界的,那么其默认边界就是Object(向上转型),由于java泛型的机制,那些边界为object的方法只能调用Object的方法,如果要想泛型做更多的事,就必须设置其边界。使其转为更具体的类型,从而表现更多的行为。
1.1上界
<? extends MyClass> 表示继承自myClass这个类的类型。
?号表示某种特定的类型通配符,这个我们后面介绍,这是指定上界常用一种方式,MyClass表示具体的类。下面我通过一个例子了解这个上边界的用法。
<? extends Fruit> 可以让变量相对泛话,如下,集合泛型参数是确定的,即使fruit和app存在继承关系,但要是把apple的list赋给fruit list变量还是会报错,因为fruit list并不是apple list,但是使用? extends Fruit list变量接受,可以解决这个问题
//List<Fruit> fruitList = new ArrayList<Apple>(); error 参数类型不匹配
List<? extends Fruit> fruits = new ArrayList<Apple>();
class Fruit{
private static int count=0;
public String toString(){
return this.getClass().getSimpleName() + " " +count++;
}
}
class Apple extends Fruit{
}
class Orange extends Fruit{
}
class Banana extends Fruit{
}
class Pear extends Fruit{
}
public class GeneratorBound{
public static void main(String[] args){
List<Fruit> list = new ArrayList<Fruit>();
List<? extends Fruit> list1 = list;
//list1.add(new Fruit()) //error
//list1.add(new Apple())// error
//list1.add(new Object())//error
Fruit f = list1.get(0);
}
}
这有个Apple,pear等类的父类是Fruit类,
我们实例化了一个Fruit类型的集合list,又声明了一个引用变量list1,这个集合的类型使用来上面的表达式,表示继承自Fruit的集合,并将其指向list。但是这里list1的add方法,却不能添加Fruit或Fruit的子类,按理来说向上转型,应该是可以添加的,这是为什么呢?原因是java的泛型是由"擦除实现",编译器在编译期就必须确定这个集合的类型,对于list1来说,由于并不知道Fruit的子类型到底有哪些,相当于向下开了一个口子,为了保证添加类型的安全,所以add方法只能添加null,那这样的类型有什么用呢,所以使用了**<? extends Fruit>,只适合拿来获取元素,不适合设置元素** 。
Fruit f = list1.get(0);
list1适合用来取元素。因为向上转型是安全的。
1.2下界(逆变)
<? super MyClass> 也可以使用类型参数<? super T>,前者表示Myclass的超类,后者表示泛型参数超类。我们还是通过刚才那个例子来看看
public class GeneratorBound{
public static void main(String[] args){
List<Fruit> list = new ArrayList<Fruit>();
List<? super Fruit> list1 = list;
list1.add(new Fruit());
list1.add(new Apple());
//list1.add(new Object()); Type mismatch: cannot convert from capture#3-of ? super Fruit to Fruit
// Fruit f = list1.get(0);
}
}
list1表示Fruit父类的集合,但是我们确发现list1可以添加Fruit的子类,但却不能添加Fruit的父类,这不是和我们上面的描述矛盾?,因为list1并不知道Fruit的父类有哪些,相当于向上开了一个口子,如果可以添加Fruit或Fruit的父类,将无法安全的添加元素,而添加子类向上转型为Fruit,是安全的。和<? extends Fruit>是相反的。所以向集合添加元素可以使用<? super MyClass>,而取元素使用<? extends Fruit>更为方便。造成这些原因都是因为java的泛型是由”擦除“实现的。这也是java泛型不够强大的原因。
1.3?(通配符)
表示某个特定类型的非原生类型,例如List表示持有任何类型的非原生List,而List<?>表示某种特定类型的非原生List,在很多情况下2者可以表现出相同的性质。使用?会导致确切的类型信息丢失,从而表现出足够泛话,要使用特定类型则需要强转。
public class GeneratorBound{
public static void main(String[] args){
List<Fruit> list = new ArrayList<Fruit>();
List<?> list2 = list;
//list2.add(new Object()); error 不知道是什么类型,所以即使是Object类型也无法添加进容器。
Object obj = list2.get(0);//取出来向上转型为Object类型
}
}