在泛型中使用通配符和繼承
本文是Sun官方以Blog形式發(fā)布的Java核心技術(shù)竅門(JavaCoreTechTip)中的一篇,它以非常簡潔的示例展示了泛型通配符的使用,初學(xué)Java泛型的朋友可以看看。(2009.12.30最后更新)Java2平臺(tái),標(biāo)準(zhǔn)版5.0(J2SE 5.0)為Java程序設(shè)計(jì)語言及其平臺(tái)引入了泛型。在最簡單的案例和典型的應(yīng)用中,泛型能夠識(shí)別集合容器中所存儲(chǔ)的是否是你所期望的對(duì)象。所以,你可以特別說你有一個(gè)String或其它類型對(duì)象的List,而不是聲稱你的程序有一個(gè)Object的List。所以,如果你不小心向該List中加入了錯(cuò)誤類型的對(duì)象,編譯器會(huì)告之你這個(gè)錯(cuò)誤。該錯(cuò)誤將在編譯時(shí)進(jìn)行修復(fù),而不用等到你運(yùn)行該程序,且在程序運(yùn)行到該處代碼時(shí),在獲取對(duì)象的操作中產(chǎn)生一個(gè)運(yùn)行時(shí)的強(qiáng)制類型轉(zhuǎn)換異常。
這就提出了泛型的第二個(gè)好處。迭代器將變得類型安全了。Iterator接口中的next()方法將會(huì)返回集合中下一個(gè)元素的類型安全版本。
但這并不是本文要介紹的泛型應(yīng)用的竅門,那些竅門已由2005 Core Java Technologies Tip描述過了。在使用泛型時(shí),大多數(shù)人都不能很好地理解對(duì)extends關(guān)鍵字的使用。一個(gè)典型的描述如何使用extends關(guān)鍵字的示例與繪制圖形有關(guān)。與其不同的是,此處竅門所用的示例將使用Swing組件,以便你不必創(chuàng)建額外的新類。在一個(gè)非常有限的例子中,Swing按鈕組件的類層次結(jié)構(gòu)如下所示,當(dāng)然,Object是實(shí)際上的根。
Component
|- Container
|- JComponent
|- AbstractButton
|- JButton
|- JMenuItem
|- JCheckBoxMenuItem
|- JMenu
|- JRadioButtonMenuItem
|- JToggleButton
|- JCheckBox
|- JRadioButton
|- Container
|- JComponent
|- AbstractButton
|- JButton
|- JMenuItem
|- JCheckBoxMenuItem
|- JMenu
|- JRadioButtonMenuItem
|- JToggleButton
|- JCheckBox
|- JRadioButton
所有AbstractButton的子類都共同享有的一個(gè)東西就是方法getText。這就是泛型的精髓,你能定義一個(gè)方法去處理以AbstractButton為元素的List,并返回這些按鈕的String類型的標(biāo)簽的List。下面是該方法的第一個(gè)版本:
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;
}
List<String> labelList = new ArrayList<String>(list.size());
for (AbstractButton button: list) {
labelList.add(button.getText());
}
return labelList;
}
下面就是如何使用該方法。首先,定義一個(gè)AbstractButton類型的List,然后向其中填充值,并調(diào)用該方法:
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);
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);
根據(jù)Google,"Hola, Mundo"是"Hello, World"的西班牙譯文。調(diào)用println()方法的結(jié)果如下所示:
[Hello, World, Hola, Mundo]
對(duì)于AbstractButton的List對(duì)象,一切都能正常運(yùn)行,但當(dāng)是其它類型,特別是AbstractButton子類型的List時(shí),就不能正常工作了。從邏輯上,有人可能認(rèn)為對(duì)于以JButton為元素的List,一切仍能正常工作。因?yàn)镴Button是AbstractButton的子類。難道不能對(duì)AbstractButton子類型的List調(diào)用方法getLabels(List<AbstractButton>)?
然而,事實(shí)并非如此。因?yàn)檫@是一個(gè)編譯時(shí)檢查,同時(shí)也因?yàn)間etLabels方法被定義為只接受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
in GetList cannot be applied to (java.util.List<javax.swing.JButton>)
List<String> labels = getLabels(buttonList);
^
1 error
這也就是extends關(guān)鍵字發(fā)揮作用的地方了。不將getLabes方法定義為僅僅接受AbstractButton List,而是將它定義為接受AbstractButton子類的List:
public static List<String> getLabels(
List<? extends AbstractButton> list)
List<? extends AbstractButton> list)
此處的通配符?表明該方法并不關(guān)心確切的類型是什么,只要它是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;
}
}
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;
}
}
現(xiàn)在,當(dāng)你要用泛型來定義你自己的類和方法時(shí),就要考慮接受作為泛型參數(shù)的抽象類,或它的任一超類,記得使用通配符以便相同的方法對(duì)于子類也能很好地工作。
更多關(guān)于泛型的信息,請見兩篇較早前由Gilad Bracha撰寫的教程:一篇是2004年的教程(PDF),另一篇是在線的Java Tutorial中的泛型章節(jié)。
posted on 2009-12-28 13:44 John Jiang 閱讀(2103) 評(píng)論(0) 編輯 收藏 所屬分類: JavaSE 、Generics 、Java 、翻譯 、CoreJavaTechTips