通配符的一个好处是允许编写可以操作泛型类型变量的代码,并且不需要了解其具体类型。
--------------------------------------------------
例如
demo<String> d = new demo<String>();
d.setName("a");
demo<?> d1 = d;
System.out.println(d1.getName());//这儿也可以取..就是不能赋值..
d1.setName("sss");//这儿设置不了值呢? 不是?通配符类似于Object的吗?
//这是什么原理呢?大家来解释解释..
--------------------------------------------------
其实上面语句能做许多工作:它能调用 get() 方法,并且能调用任何从 Object 继承而来的方法(比如 hashCode())。
它惟一不能做的事是调用 set() 方法,这是因为在不知道该 Demo 实例的类型参数 T 的情况下它不能检验这个操作的安全性。
由于 d1 是一个 Demo<?> 而不是一个原始的 Demo,编译器知道存在一些 T 充当 demo 的类型参数,
但由于不知道 T 具体是什么,您不能调用 set() ,因为不能检验这么做不会违反 Demo 的类型安全限制
(实际上,您可以在一个特殊的情况下调用 set():当您传递 null 字母时。我们可能不知道 T 类型代表什么,但我们知道 null 字母对任何引用类型而言是一个空值)。
--------------------------------------------------
关于 d1.get() 的返回类型,d1 了解哪些内容呢?
它知道 demo.get() 是某些未知 T 的 T,因此它可以推断出 get() 的返回类型是 T 的擦除(erasure),
对于一个无上限的通配符就是 Object。因此上面的表达式 d1.getName() 具有 Object 类型,可以打印a。
--------------------------------------------------
d1.setName("sss"):为什么报错?(“capture#1 of ?”)
“capture#1 of ?” 表示什么?
当编译器遇到一个在其类型中带有通配符的变量,
比如 Demo<?> d1,它认识到必然有一些 T ,对这些 T 而言 d1 是 Demo<T>。
它不知道 T 代表什么类型,但它可以为该类型创建一个占位符来指代 T 的类型。
占位符被称为这个特殊通配符的捕获(capture)。
这种情况下,编译器将名称 “capture#1 of ?” 以 d1 类型分配给通配符。
每个变量声明中每出现一个通配符都将获得一个不同的捕获,因此在泛型声明 foo(Pair<?,?> x, Pair<?,?> y) 中,编译器将给每四个通配符的捕获分配一个不同的名称,因为任意未知的类型参数之间没有关系。
错误消息告诉我们不能调用 Set(),因为它不能检验 set() 的实参类型与其形参类型是否兼容:
因为形参的类型是未知的。在这种情况下,由于 ? 实际表示 “?extends Object” ,
编译器已经推断出 d1.get() 的类型是 Object,而不是 “capture#1 of ?”。
它不能静态地检验对由占位符 “capture#1 of ?” 所识别的类型而言 Object 是否是一个可接受的值。
|