参数声明
List<? super Dog> list = new ArrayList<Animal>(); //正确 List<? super Dog> list = new ArrayList<Object>();//因为 Object 是任何一个类的父级。正确list元素的类型可以是任何Dog的父级,JVM在编译的时候当然是无法确定具体是哪个类型,但是可以确定的是任何的Dog的子类都可以转为Dog类,而任何的Dog的父类都不能转为Dog类。
所以,若使用了super通配符,则只能存入T类型及T类型的字类对象:
list.add(new Dog());//可以添加 list.add(null);//编译正常 list.add();//编译错误bu Dog dog = list.get(0); //错误 Animal dog = list.get(0);//错误 Object dog = list.get(0);//正确用法取出数据的时候,JVM在编译时并不能确定具体的父级,所以安全起见,就用顶级的父级Object来取出数据。这样就可以避免发生强制类型转换异常了。也只能使用Object取数据。
? 无边界通配符使用的形式是一个单独的 ? ,表示无任何的限定。
List<?> list 表示 list 是持有某种特定类型的 List,但是不知道具体是哪种类型,因此时不安全的,即不能添加数据。但是可以用来取数据。
?和 T 的区别先看一下代码:
//指定集合元素只能时T类型。d但是这个d List<T> list = new ArrayList<T>(); //表明集合的元素可以是任意的类型 List<?> list = new ArrayList<?>(); public <T> void test(){ List<T> list = new ArrayList<T>();//指定集合元素只能是T类型,但是必须得配合方法使用,否则报错 } //表明集合的元素可以是任意的类型,没什么意义,一般在方法中只是为了说明用法 List<?> list = new ArrayList<?>();但是不管用T还是用? ,它们的共同点都是不能往list里添加数据,且在获取数据的时候只能用Object来接收。
其实,? 和T都是表示不确定的类型,区别在于我们可以对 T 进行操作,但是对 ?不行,比如如下这种 :
T t = operate(); //可以 ? opa = operate();//不可以T通常用于泛型类和泛型方法的定义。
?通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。
区别1:通过 T 来 确保 泛型参数的一致性例如
//通过T来确保泛型参数的一致性。 public <T extends Number> void test(List<T> list1, List<T> list2){ //......所以这里的list集合的元素的类型是一致的 } //通配符是 不确定的,所以这个方法不能保证两个 List 具有相同的元素类型 public <? extends Number> void test2(List<? extends Number> list1,List<? extends Number> list2){ //.....这里的list的元素有可能一致,有可能不一致。 } 区别2:类型参数可以多重限定而?通配符不行比如接口Apple继承Fruits ,接口Fruit继承Botany。下面就是T的多重限定的写法。使用 &
public static <T extends Fluit & Apple> void testB(T t){ //... }使用 & 符号设定多重边界(Multi Bounds),指定泛型类型 T 必须是 Fluit和 Apple的共有子类型,此时变量 t 就具有了所有限定的方法和属性。对于通配符来说,因为它不是一个确定的类型,所以不能进行多重限定。
区别3:通配符可以使用超类限定而类型参数不行类型参数 T 只具有 一种 类型限定方式:
T extends A但是通配符 ? 可以进行 两种限定:
? extends A ? super A 总结通配符的使用可以对泛型参数做出某些限制,使代码更安全,对于上边界和下边界限定的通配符总结如下:
使用通配符对泛型参数做出限制,能是代码更加的安全。
上下边界限定的通配符总结如下:
使用 List<? extends C> list 的形式,表示该元素类型的范围必须是 C 的字类( 包含 C 本身)。
使用 List<? super C> list 形式,表示该元素类型是 C 的超类型 ( 包含 C 本身 )。