John Jiang

a cup of Java, cheers!
https://github.com/johnshajiang/blog

   :: 首页 ::  :: 联系 :: 聚合  :: 管理 ::
  131 随笔 :: 1 文章 :: 530 评论 :: 0 Trackbacks
在泛型中使用通配符和继承
    本文是Sun官方以Blog形式发布的Java核心技术窍门(JavaCoreTechTip)中的一篇,它以非常简洁的示例展示了泛型通配符的使用,初学Java泛型的朋友可以看看。(2009.12.30最后更新)
    Java2平台,标准版5.0(J2SE 5.0)为Java程序设计语言及其平台引入了泛型。在最简单的案例和典型的应用中,泛型能够识别集合容器中所存储的是否是你所期望的对象。所以,你可以特别说你有一个String或其它类型对象的List,而不是声称你的程序有一个Object的List。所以,如果你不小心向该List中加入了错误类型的对象,编译器会告之你这个错误。该错误将在编译时进行修复,而不用等到你运行该程序,且在程序运行到该处代码时,在获取对象的操作中产生一个运行时的强制类型转换异常。
这就提出了泛型的第二个好处。迭代器将变得类型安全了。Iterator接口中的next()方法将会返回集合中下一个元素的类型安全版本。
    但这并不是本文要介绍的泛型应用的窍门,那些窍门已由2005 Core Java Technologies Tip描述过了。在使用泛型时,大多数人都不能很好地理解对extends关键字的使用。一个典型的描述如何使用extends关键字的示例与绘制图形有关。与其不同的是,此处窍门所用的示例将使用Swing组件,以便你不必创建额外的新类。在一个非常有限的例子中,Swing按钮组件的类层次结构如下所示,当然,Object是实际上的根。
Component
|- Container
   
|- JComponent
      
|- AbstractButton
         
|- JButton
         
|- JMenuItem
            
|- JCheckBoxMenuItem
            
|- JMenu
            
|- JRadioButtonMenuItem
         
|- JToggleButton
            
|- JCheckBox
            
|- JRadioButton

    所有AbstractButton的子类都共同享有的一个东西就是方法getText。这就是泛型的精髓,你能定义一个方法去处理以AbstractButton为元素的List,并返回这些按钮的String类型的标签的List。下面是该方法的第一个版本:
public static List<String> getLabels(List<AbstractButton> list) {
    List
<String> labelList = new ArrayList<String>(list.size());
    
for (AbstractButton button: list) {
        labelList.add(button.getText());
    }
    
return labelList;
}

    下面就是如何使用该方法。首先,定义一个AbstractButton类型的List,然后向其中填充值,并调用该方法:
List<AbstractButton> buttonList = new ArrayList<AbstractButton>();
buttonList.add(
new JButton("Hello"));
buttonList.add(
new JCheckBox("World"));
buttonList.add(
new JRadioButton("Hola"));
buttonList.add(
new JMenuItem("Mundo"));

List labels 
= getLabels(buttonList);
System.out.println(labels);

    根据Google,"Hola, Mundo"是"Hello, World"的西班牙译文。调用println()方法的结果如下所示:
[Hello, World, Hola, Mundo]

    对于AbstractButtonList对象,一切都能正常运行,但当是其它类型,特别是AbstractButton子类型的List时,就不能正常工作了。从逻辑上,有人可能认为对于以JButton为元素的List,一切仍能正常工作。因为JButton是AbstractButton的子类。难道不能对AbstractButton子类型的List调用方法getLabels(List<AbstractButton>)
然而,事实并非如此。因为这是一个编译时检查,同时也因为getLabels方法被定义为只接受AbstractButton的List,你不能向该方法中传入任何其它类型的List。
GetList.java:13: getLabels(java.util.List<javax.swing.AbstractButton>)
  in GetList cannot be applied to (java.util.List
<javax.swing.JButton>)

    List
<String> labels = getLabels(buttonList);
                          
^
1 error

这也就是extends关键字发挥作用的地方了。不将getLabes方法定义为仅仅接受AbstractButton List,而是将它定义为接受AbstractButton子类的List
public static List<String> getLabels(
        List
<? extends AbstractButton> list)

此处的通配符?表明该方法并不关心确切的类型是什么,只要它是AbstractButton的子类型即可。下面是综合了前述所有代码片断的完整示例程序:
import java.util.*;
import javax.swing.*;

public class GetList {
    
public static void main(String args[]) {
        List
<JButton> buttonList =
                
new ArrayList<JButton>();
        buttonList.add(
new JButton("Hello"));
        buttonList.add(
new JButton("World"));
        buttonList.add(
new JButton("Hola"));
        buttonList.add(
new JButton("Mundo"));

        List labels 
= getLabels(buttonList);
        System.out.println(labels);
    }

    
public static List<String> getLabels(
            List
<? extends AbstractButton> list) {
        List
<String> labelList = new ArrayList<String>(list.size());
        
for (AbstractButton button: list) {
            labelList.add(button.getText());
        }
        
return labelList;
    }
}

现在,当你要用泛型来定义你自己的类和方法时,就要考虑接受作为泛型参数的抽象类,或它的任一超类,记得使用通配符以便相同的方法对于子类也能很好地工作。
更多关于泛型的信息,请见两篇较早前由Gilad Bracha撰写的教程:一篇是2004年的教程(PDF),另一篇是在线的Java Tutorial中的泛型章节。

posted on 2009-12-28 13:44 John Jiang 阅读(2090) 评论(0)  编辑  收藏 所属分类: JavaSEGenericsJava翻译CoreJavaTechTips

只有注册用户登录后才能发表评论。


网站导航: