声明:文中描述的某个类对象的引用,该引用指向的对象并非其子类的对象,而是真真实实的这个类的对象。
类层次关系
----------------------------
1. 下界通配符测试(代码块1-1)
void useLowerbBoundWildCard(List<? super GuidedFireArrow> superGuidedFireArrowList)
{
/*for(Arrow arrow:superGuidedFireArrowList)
{
}*/
//读取上界
for(int i=0;i<superGuidedFireArrowList.size();i++)
{
//实例所属类型判断
if(superGuidedFireArrowList.get(i).getClass().equals(Arrow.class))
{
System.out.println("this instance belong to class Arrow");
}
else if(superGuidedFireArrowList.get(i).getClass().equals(FireArrow.class))
{
System.out.println("this instance belong to class FireArrow");
}
else if(superGuidedFireArrowList.get(i).getClass().equals(GuidedFireArrow.class))
{
System.out.println("this instance belongy to class GuildedFireArrow");
}
else
{
System.out.println("unexpected instance");
}
}
//下界写入
superGuidedFireArrowList.add(new GuidedFireArrow());
}
1.1 分析
List<? super GuidedFireArrow> 表示这样的一个列表:
存放的对象的引用都是该类的父类或者父类的父类乃至祖先类的对象。
因此下面列出的所有列表:
- List<Arrow>
- List<FireArrow>
- List<GuildedIFireArrow>
- List<Object>
都可以是一个List<? super GuidedFireArrow>。
那么从 List<? super GuidedFireArrow> 读出来的元素可能是一个Object类的一个对象引用,可能是Arrow类的一个对象引用,也可能是一个FireArrow类的对象引用,或者一个GuildedFireArrow类的一个对象引用。由于不知道确切的是什么对象的引用,故使用时还需要判断一下对象的类型,使用getClass来判断(不用instanceof或者isInstance方法来判断,因为这两个方法即使作为参数的引用指向的是其子类的对象也会判断是这个类的实例)。
可以往这个列表中加入任意类型的GuidedFireArrow类,因为不管列表中存放的是哪个类,指向GuidedFireArrow类对象的引用都可以隐式地转换(列表中存放的对象引用所属的类必然是GuidedFireArrow的父类引用,或者它本身)为这个类的引用。
1.2 foreach循环应用于List<? super E>
for(Arrow arrow:superGuidedFireArrowList)
{
}
在代码块1-1中有这样一段被注释掉的for循环,使用这段代码时,会报错。原因在于:
从列表List<? super GuidedFireArrow> 中取出的引用,不见得就一定能转换为类Arrow的引用,虽然类Arrow是FireArrow 、GuidedFireArrowList这些类的父类或者祖先类,但是Arrow还有一个父类是Object类,因此如果这个list中存放的是Object类对象的引用,且引用指向的确实是Object对象,则等于把一个Object类对象引用转换为一个Arrow类对象引用这种是会触发ClassCastException异常。为了避免运行时出现错误,编译器索性在编译时就报错。
2. 上界通配符测试(代码块2-1)
void test(List<? extends Arrow> arrowList)
{
for(Arrow arrow:arrowList)
{
}
}
2.1 分析
List<? extends Arrow> 表示这样的一个列表:
存放的对象的引用都是继承Arrow类的子类的对象或其本身的对象的引用。
因此下面列出的所有列表:
- List<Arrow>
- List<FireArrow>
- List<GuildedIFireArrow>(注意,即使是子类的子类,也算是继承这个类)
- List<IceArrow>
都可以是一个List<? extends Arrow>。
因此从这个列表中取出的每个引用都可以转换为Arrow引用。
但是一旦出现这样的代码:
void test(List<? extends Arrow> arrowList)
{
arrowList.add(new Arrow());
}
IDE就会报错,因为这个List存放的对象引用可能是上述的某种,所以一旦一个存放FireArrow类对象的List被传入这个函数,然后想往其中添加一个Arrow类对象引用或者IceArrow对象引用,都会报错(运行时可能出现ClassCastException异常)。而无限制通配符List<?> 中存放的对象引用更不知道是什么类型的,同样禁止往其中添加任何对象引用,除了可以表示任何对象引用的null引用。
结论
Effective Java有个"PECS"原则,即
producer-extends,consumer-super
使用上界通配符 <? extends T> 的容器通常可以作为一个生产者,只能往外面吐东西,而不能往里面添加任何东西(除了null,null可以是任何对象的引用),使用下界通配符 <? super T> 的容器可以作为一个消费者,可以往里面写入东西,也可以往外面吐东西(相对来说比较麻烦,因为不清楚吐出的东西到底是什么对象的引用)。
本文深入探讨Java泛型的上界和下界通配符的使用,解析List<?superT>和List<?extendsT>的区别与应用场景。通过具体示例,阐述如何利用PECS原则进行高效编程。
1964

被折叠的 条评论
为什么被折叠?



