交互操作性(Interoperability)
在本課程中,你將學習兩個方面的交互操作性:
兼容性
本課程將向你演示如何使對象集與先於對象集添加到Java平台上的舊的API一起工作。
API 設計
教給你如何設計一個新的 API,使它們之間可無縫交互操作。
兼容性
設計Collection Framework 是為了保證在新的 對象集 接口 和傳統的被用來表示 對象集 的類型之間的完全的交互操作性: Vector, Hashtable, array, 和Enumeration。在本節中,你將學習如何將傳統的 對象集 轉換為新的對象集以及相反的過程。
向上兼容性
假設你要使用一個將返回傳統 對象集 的 API,同時還要使用另一個API,這個API要求對象實現JDK1.2的對象集接口。為使這兩個API可以平滑地交互操作,你必須將傳統的 對象集 轉化為新的 對象集。幸運的是,Collection Framework 使這個工作變得十分簡單。
假設舊的API 返回一個對象數組,而新的API需要一個 Collection。正象在 實現課程中所討論的,對象集 架構 具有一個便利實現,它允許一個對象數組被當作一個 List。使用Arrays.asList, 一個數組可被傳遞給需要一個 Collection 或一個 List 的任意方法。Foo[] result = oldMethod(arg);
newMethod(Arrays.asList(result));
如果舊的API 返回一個 Vector 或 Hashtable, 你就一點工作都不需要做了,因為 Vector 已被改造為實現了 List 接口,而 Hashtable 也被改造為實現了 Map。於是,一個 Vector 可被直接傳遞給需要一個 Collection 或一個 List 的任意方法。
Vector result = oldMethod(arg);
newMethod(result);
類似的,一個 Hashtable 可被直接傳遞給需要一個 Map 的任意方法。
Hashtable result = oldMethod(arg);
newMethod(result);
比較少見的是,一個API 返回一個 Enumeration ,它代表了一個對象的 對象集。雖然沒有對將 Enumeration 轉換為Collection 的直接支持,但要創建包含由Enumeration 返回的所有元素的 Collection ,也是一個簡單的事情:
Enumeration e = oldMethod(arg);
List l = new ArrayList();
while (e sMoreElements())
l.add(e.nextElement());
newMethod(l);
向後兼容
假設你要使用一個將返回 "新的 對象集" 的API,同時還要使用一個需要你傳遞 "傳統的 對象集" 的API。為使這兩個API平滑地交互操作,你必須將新的 對象集 轉化為傳統的對象集。同樣地,Collection Framework 使這個工作十分簡單。天呀,今天是你的幸運日吧?
假設新API返回一個 Collection, 而舊的API 需要一個Object數組。你可能已經知道了,Collection 接口包含一個 toArray 方法,它明顯的是為此種情況設計的:
Collection c = newMethod();
oldMethod(c.toArray());
如果舊的API 需要一個 String 數組(或某些其它類型) 而不是 Object數組,那又該怎麼辦呢?不要喪失希望。你只要使用 toArray 的其它形式--在輸入上使用數組的形式就可以了:
Collection c = newMethod();
oldMethod((String[]) c.toArray(new String[0]));
如果舊的API 需要一個 Vector, 那麼就使用手頭的標准對象集構造函數好了:
Collection c = newMethod();
oldMethod(new Vector(c));
舊的 API 需要一個 Hashtable 的情況可被對等地處理:
Map m = newMethod();
oldMethod(new Hashtable(m));
最後,如果舊的API 需要一個 Enumeration,你該怎麼辦呢?這種情況不普遍,但它確實時有發生。Collections.enumeration 方法可用來處理此中情況。這個方法是個靜態方法,它采用一個Collection並返回一個在 Collection 的元素之上的 Enumeration :
Collection c = newMethod();
oldMethod(Collections.enumeration(c));
API 設計
在這個雖短但卻很重要的一節裡,你將學習幾個簡單的准則,它使你的 API 可以與所有其它的遵循這一准則的精良的 API 無縫地交互操作。實質上,這些准則正是在對象集這個勇敢者的新世界裡,作為一個好公民所必需的條件。
輸入參數(in-Parameters)
如果你的API包含一個需要輸入一個 對象集 的方法,十分重要的一點是,你應該將相關的參數類型聲明為 對象集接口類型之一。絕不使用實現類型, 它允許 對象集 在不考慮實現細節的情況下被操縱,因而會使一個基於接口的對象集架構的目標流產。
進一步講,你應該總是使用最不特殊的有意義的類型。例如,如果可以使用 Collection,則不要使用 List 或 Set 。但並不是要你絕不使用一個 List 或一個 Set ;如果一個方法依賴於這些接口之中的某些屬性,則應該使用它們。例如,Java平台的許多 algorithms 要求輸入一個 List ,因為它們依賴於列表是有順序的這樣一個事實。然而作為一般法則,在輸入上使用的最好類型是最一般的: Collection 和 Map。
注意: 絕不、絕不定義你自己的特別的 對象集 類並要求輸入這個類的對象。如果這樣做,你會失去所有 由對象集架構所提供的益處。
返回值
返回值的靈活性要比輸入參數大得多。你可以返回任意類型的一個對象,用它來實現或擴展某一個 對象集 接口。這可能是接口本身之一,或是擴展(或實現)某一個接口的某些特種類型。
例如,你可能想象要建立一個被稱作ImageList的圖象處理程序包,它返回一個新的實現了 List 的類的對象。除支持 List 操作之外,ImageList 應可以支持任意特殊應用程序操作。例如,它可能提供一個 indexImage 操作,它可以返回一個將 ImageList 中的每一個圖形變成指甲蓋大小,然後集中在一起的一個圖象。非常值得注意的一點是,即使 API 可提供 ImageList 對象輸出,它也應該接受任意 Collection (或也許是 List) 對象的輸入。
從某種意義上講,返回值應該有一個與輸入參數相對的行為:最好返回最特殊的和可適用的 對象集 接口,而不是最一般的。例如,如果你十分有把握由某些方法所返回的 Map 總是一個SortedMap, 你就應該將 SortedMap 的返回類型賦予這個方法,而不是 Map。SortedMap 對象比普通 Map 對象的建立更費時,但功能也更強大。假設你的模塊已經投資了一定時間來建立一個 SortedMap, 那麼最好給用戶以訪問增加的功能的權利。進一步講,用戶將能夠將返回的對象傳遞給那些要求 SortedMap 的方法,以及那些接受任意 Map 的方法。
再一次提醒你,絕不、絕不定義你自己的特別的 對象集 類並提供這個類的對象作為一個返回值。如果這樣做,你將會失去所有由對象集架構提供的益處 (有點似曾相識嗎?)
早期API(Legacy API)
現在有許多 API ,它們定義了自己特有的對象集 類型。雖然這是不幸的,但這就是事實, 因為在Java平台的前兩個主要版本中沒有 對象集 架構。假設你現在擁有一個那樣的API。以下是你可以作的事情。如果可能的話,重新更改你的早期 對象集 類型以實現某一個標准 對象集 接口。這樣,你返回的所有對象集 將與其它基於 對象集 的 API 平滑地交互操作。如果這是不可能的(例如,由於一個或幾個先前存在的類型與標准對象集接口發生沖突), 定義一個adapter class(適配器類),它包裝你的早期 對象集 對象,使其表現出一個標准 對象集 的功能 (適配器類是定制實現中的一個例子)。
如果可能的話,用新的符合輸入准則的調用來重新更改你的API ,使其可以接受標准對象集接口。這樣的調用可與采用早期對象集 類型的調用共同存在。如果這是不可能的,為你的早期類型提供一個構造函數或靜態方法,它將使用標准接口之一的一個對象,並返回一個包含相同元素(或映射)的早期 對象集 。這兩種方式都將允許用戶將任意 對象集 傳遞到你的 API。
Collections: 結束
你已經到達 "Collections" 課程的終點。
休息一下--來一杯熱氣騰騰的Java吧!