一直没注意这方面的内容,想来这也算是基础了,就写了这个笔记。
首先java的通配符共有三种————先别紧张,现在只是粗略的过一下,看不看其实无所谓
类型 | 介绍 |
---|---|
<?> | 无限定通配符,等价于 <? extends Object> |
<? extends Number> | 上限通配符,表示参数类型只能是 Number 或是 Number 的子类。 |
<? super Number> | 下限通配符,表示参数类型只能是 Number 或是 Number 的父类。 |
然后再让我们定义四个类,下面会用到
- class A {
- public String getName() {
- return "A";
- }
- }
- class B extends A{
- @Override
- public String getName() {
- return "B";
- }
- }
- class BAge extends B{
- @Override
- public String getName() {
- return "C";
- }
- public int getAge() {
- return 100;
- }
- }
- class BSize extends B{
- @Override
- public String getName() {
- return "D";
- }
- public int getSize() {
- return -1;
- }
- }
- class PrintAges{
- public static void print(BAge[] ages){
- if (ages == null)
- return;
- for (BAge bage : ages){
- if (bage != null)
- System.out.println(bage.getAge());
- }
- }
- }
怎样?够完美吗,不会引发异常吧?我觉得也很完美了,肯定不会有异常出现在我的代码里了。
- PrintAges
- BAge[] temps = new BAge[]{new BAge(), new BAge()};
- PrintAges.print(temps);
输出:
- 100
- 100
完美运行。
- BAge[] temps = new BAge[]{new BAge(), new BAge()};
- B[] barray = temps; // 新增加的第一行
- barray[0] = new BSize(); // 新增加的第二行
- PrintAges.print(temps);
输出:
- Exception in thread "main"java.lang.ArrayStoreException: JavaApp.BSize at JavaApp.JavaApplicationStudyGen.main(JavaApplicationStudyGen.java: 33)
本来我以为会在 PrintAges 的 print 方法中发生异常,但是实际上新增加的第二行发生了运行时错误,赋值错误。
- 而在C#中,这种问题出现的可能性就更小了。C#中,新增的第一行是无法通过编译的。
那么,这种问题在集合……准确地说是在泛型里会不会出现呢?
我们先对
添加一个 print 函数的重载
- PrintAges
- class PrintAges{
- public static void print(ArrayList<BAge> list) {
- if (list == null)
- return;
- for(BAge age : list) System.out.println(age.getAge());
- }
- public static void print(BAge[] ages){
- if (ages == null)
- return;
- for (BAge bage : ages){
- if (bage != null)
- System.out.println(bage.getAge());
- }
- }
- }
然后我们对用再次运行如下代码:
- ArrayList < BAge > list = new ArrayList < BAge > ();
- list.add(new BAge());
- ArrayList < B > yourList = list; // 编译错误
- yourList.set(0, new BSize()); // star 1
- BAge age = list.get(0); // star 2
- PrintAges.print(list);
这次,Java 处理的比较严格,在把
赋值给
- ArrayList<BAge>
类型的对象时产生了编译错误。
- ArrayList<B>
- 在C#里,也是一样的,在把ArrayList < BAge > 赋值给ArrayList < B > 类型的对象时会产生编译错误。
一开始,我不理解这样做对 list 引用的对象 ArrayList
但是,不能赋值的原因,把一个 BSize 类型的对象放在了一个实际上是 ArrayList
经过类型擦除之后,
所在行的代码就会变成
- star 2
- BAge age = (BSize)list.get(0); // star 2
这样就是完全不正确的了。
也就是说,我们应该禁止类似
这样的赋值,否则,就会出现这样的错误和意外。
- ArrayList<B> yourList = new ArrayList<BAge>()
说实话,
这样的赋值操作也该被禁止的,但是 Java 就可以。
- B[] barray = new BAge[]{new BAge(), new BAge()}
记住这样的错误。接下来,我们就可以讨论 Java 的泛型通配符了。
所以所,通配符的出现就是为了在错误避免上述错误的同时,给程序员提供一点便利。
而通配符是怎么样发生作用的呢?是通过编译器给定的三条“游戏规则”(也即是上面给的表格里的规则)发生作用的。
在一开始理解的时候是需要一点逻辑能力的:
确保了可读性,
- <? extends B>
可以被编译通过的语句:
- <? extends B> 表示参数类型只能是 B 或是 B 的子类
- ArrayList<? extends B> list = new ArrayList<A>(); // 编译错误
- ArrayList<? extends B> list = new ArrayList<B>(); // ok
- ArrayList<? extends B> list = new ArrayList<BAge>(); // ok
- ArrayList<? extends B> list = new ArrayList<BSize>(); // ok
基于以上的编译规则,我们可以得出以下事实:
,要么指向包含 B 子类对象的
- ArrayList<B>
- ArrayList<B>
或者指向
- ArrayList<BSize>
- ArrayList<BAge>
- ArrayList<BSize>
- ArrayList<BAge>
注意,上述代码中, list 中的
被替换成了
- T
- ? extends B
也就是说,读取操作可以被确保,你一定能从 list 中读取到一个 B 元素 这样,
方法就可以被正常使用了。
- list.get
而
就被替换成了
- list.set(int, T)
,这个方法就被编译器“禁止”了。也就是说,如果你写出
- list.set(int, ? extends B)
或
- list.set(0, new B())
是不行的。
- list.set(0, new BSize())
在这里你肯定要提出疑问了,你不是说符合“游戏规则”
就行的吗? 我只能说,文字所能传达的信息是有限的,这个表述也只适用于
- <? extends B> 表示参数类型只能是 B 或是 B 的子类
这样的赋值时刻。还是得看上述推导的“事实”
- ArrayList<? extends B> list = new ArrayList<A>();
确保了写入性
- <? super B>
- ArrayList<? super B> list = new ArrayList<Object>(); // ok
- ArrayList<? super B> list = new ArrayList<A>(); // ok
- ArrayList<? super B> list = new ArrayList<B>(); // ok
- ArrayList<? super B> list = new ArrayList<BAge>(); // 编译错误
- ArrayList<? super B> list = new ArrayList<BSize>(); // 编译错误
基于以上的编译规则,我们可以得出以下事实:
,要么指向包含 B 超类型的 ArrayList 对象,比如: list 可能是
- ArrayList
或
- ArrayList<Object>
。
- ArrayList<A>
或者是
- ArrayList<Object>
- ArrayList<B>
这样,
方法就可以被正常使用了。假设
- list.set
指向
- list
,我们把一个 B 类型的对象添加到
- ArrayList<Object>
中也没错啊。
- ArrayList<Object>
对象添加到
- BAge
或
- ArrayList<Object>
中也没错啊。
- ArrayList<A>
对象添加到
- BSize
或
- ArrayList<Object>
中也没错啊。
- ArrayList<A>
确保了可读性,
- ? extends B
确保了写入性。
- ? super B
和
- ? extends B
给人的感觉是逆操作。
- ? super B
来源: http://www.cnblogs.com/night-ride-depart/p/7918980.html