程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> IAdaptable是什麼?

IAdaptable是什麼?

編輯:關於JAVA

IAdaptable在Eclipse裡是一個非常重要的接口。對於Eclipse開發老手來說,它就像異常 處理和抽象類一樣尋常;但是對新手而言,它卻令人感到困惑和畏懼。這篇文章將向你解釋 IAdaptable到底是什麼,以及它在Eclipse裡起到的作用。

類型轉換

Java是所謂的強類型語言,也就是說,每個實例都對應一個類型。其實類型分為兩種:聲 明類型和運行時類型(也分別被稱為靜態類型和動態類型)。像Python這樣的弱類型語言常 被稱為無類型的語言,其實嚴格說來不是這樣,因為每個實例都對應一個運行時類型,只是 你並不需要聲明這一點而已。

現在回到Java,為了能夠執行一個類的某個方法,這個方法必須在聲明類型中可見,換句 話說,即使在運行時實例是某個子類型,你也只能執行那些父類型裡定義的方法。

List list = new ArrayList();
list.add("data");    // 正確,add是List裡定義的方法
list.ensureCapacity(4); // 不正確,ensureCapacity()只在ArrayList被定義

如果一定要執行特定類型的方法,我們必須先強制轉換這個實例到正確的類型。對於上面 的例子,我們可以將list轉換為ArrayList(譯注:原文In this case, we can cast ArrayList to List,懷疑是筆誤),因為ArrayList實現了List接口,你甚至可以在運行時 通過instanceof關鍵字檢驗list是否為ArrayList的一個實例。

可擴展的接口

不幸的是,一個類可能並沒有實現你需要的接口,這樣就無法進行強制類型轉換了。原因 有很多,比如只在少數情況下才需要這個接口,或者你需要的接口是在另一個不相關的庫裡 ,又或者接口是有了類以後才開發出來的,等等。

這時你就需要IAdaptable了。可以把IAdaptable想象為一個能夠動態進行類型轉換的途徑 。對比下面的直接類型轉換:

Object o = new ArrayList();
List list = (List)o;

換一種方式,我們可以這樣做:

IAdaptable adaptable = new ArrayList();//譯注:這裡的ArrayList應該不是指 java.util.ArrayList
List list = (List)adaptable.getAdapter(java.util.List.class);

這就是上面所說的動態類型轉換,我們所做的事情是試圖把adaptable轉換為一個List實 例。

那麼,當可以直接轉換的時候為什麼要費這個力氣通過getAdapter()來轉換呢?其實這種 機制可以讓我們將目標類轉換為它並沒有實現的接口。舉個例子,我們可能想把一個HashMap 當作List來用,盡管這兩個類的性質並不相同,可以這麼做:

IAdaptable adaptable = new HashMap();//譯注:這裡的HashMap應該不是指 java.util.HashMap
List list = (List)adaptable.getAdapter(java.util.List.class);

實現IAdaptable接口

大部分IAdaptable的實現是一些if語句的疊加,比如我們現在要實現HashMap的 getAdapter()方法,它看起來可能是這樣:

public class HashMap implements IAdaptable {
  public Object getAdapter(Class clazz) {
   if (clazz == java.util.List.class) {
    List list = new ArrayList(this.size());
    list.addAll(this.values());
    return list;
   }
   return null;
  }
  // 
}

所做的就是返回一個適配器(adapter,更確切的說是一個副本),而不是進行直接的類 型轉換。如果參數類型沒有被支持,慣例是返回null值(而非拋出異常),代表這個方法失 敗了。因此,在調用這個方法時,不應該假定它總是返回非null值。

PlatformObject

當然,如果你希望增加一個新的被支持的adapter類型時必須編輯這個類才行(譯注:在 getAdapter()裡增加更多的if語句),這會比較辛苦。而且,既然你已經知道了這個類型, 何不直接修改接口聲明呢?其實有很多原因使得你並不希望直接編輯這個類(例如更容易保 持向下兼容性),也不想改變它的類型(HashMap雖然不是一個List,但可以轉換過去)。

Eclipse通過PlatformObject抽象類來解決以上問題,它為你實現了IAdaptable接口, Eclipse平台(Platform)提供了IAdapterManager的一個實現,並且可以通過 Platform.getAdapterManager()訪問到,它把所有對getAdapter()的請求(調用)委托給一 個名為IAdapterManager的東西。你可以將它想象為一個巨大的保存著類和adapter信息的Map ,而PlatformObject的getAdapter()方法會查找這個Map。

適配已存在的類

這樣,PlatformObject不需要重新編譯就能夠支持新的adapter類型,這一點在Eclipse裡 被大量使用以支持workspace的擴展點。

現在假設我們想要將一個只包含String類型元素的List轉換為一個XMl節點,這個節點的 格式如下:

<List>
  <Entry>First String</Entry>
  <Entry>Second String</Entry>
  <Entry>Third String</Entry>
</List>

因為toString()方法可能有其他用途,我們不能通過覆蓋toString()方法來實現這個功能 。所以,我們要給List關聯一個工廠類以處理XML節點類型的適配請求。要管理工廠類需要以 下三個步驟:

1、由List生成一個Node,我們把這個轉換過程用IAdapterFactory包裝起來:

import nu.xom.*;
public class NodeListFactory implements IAdapterFactory {
  /**//* 可以轉換到的類型 */
  private static final Class[] types = {
   Node.class,
  };
  public Class[] getAdapterList() {
   return types;
  }
  /**//* 轉換到Node的功能代碼 */
  public Object getAdapter(Object list, Class clazz) {
   if (clazz == Node.class && list instanceof List) {
    Element root = new Element("List");
    Iterator it = list.iterator();
    while(it.hasNext()) {
     Element item = new Element("Entry");
     item.appendChild(it.next().toString());
     root.appendChild(item);
    }
    return root;
   } else {
    return null;
   }
  }
}

2、把這個工廠類注冊到Platform的AdapterManager,這樣當我們希望從List的實例中獲 得一個Node實例時,就會找到我們的工廠類。注冊一個工廠類的方式也很簡單:

Platform.getAdapterManager().registerAdapters(
  new NodeListFactory(), List.class
);

這條語句將NodeListFactory關聯到List類型。當從List裡請求adapter時,Platform的 AdapterManager會找到NodeListFactory,因為在後者的getAdapterList()方法的返回結果裡 包含了Node類,所以它知道從List實例得到一個Node實例是可行的。在Eclipse裡,這個注冊 步驟一般是在plugin啟動時完成的,但也可以通過org.eclipse.core.runtime.adapters擴展 點來完成。

3、從List獲得Node,下面是例子代碼:

Node getNodeFrom(IAdaptable list) {
  Object adaptable = list.getAdapter(Node.class);
  if (adaptable != null) {
   Node node = (Node)adaptable;
   return node;
  }
  return null;
}

總結

綜上所述,要在運行時為一個已有的類增加功能,所要做的只是定義一個用來轉換的工廠 類,然後把它注冊到Platform的AdapterManager即可。這種方式在保持UI組件和非UI組件的 分離方面特別有用。例如在org.rcpapps.rcpnews.ui和org.rcpapps.rcpnews這兩個plugin裡 ,前者的IPropertySource需要與後者的數據對象(data object)相關聯,當前者初始化時 ,它將IPropertySource注冊到Platform,當數據對象在導航器(navigator)裡被選中的時 候,屬性視圖裡就會顯示正確的屬性。

顯然,java.util.List並不是PlatformObject的子類,所以如果你希望能夠編譯這裡所說 的例子,必須建立一個List的子類型。注意,可以直接實現IAdaptable接口,而非必須繼承 PlatformObject抽象類。

public class AdaptableList implements IAdaptable, List {
  public Object getAdapter(Class adapter) {
   return Platform.getAdapterManager().getAdapter(this, adapter);
  }
  private List delegate = new ArrayList();
  public int size() {
   return delegate.size();
  }
  // 
}

最後,例子裡生成XML的部分使用了XOM的類庫

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved