IAdaptable是什么?

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.ArrayListList list = (List)adaptable.getAdapter(java.util.List.class);

这就是上面所说的动态类型转换,我们所做的事情是试图把adaptable转换为一个List实 例。

那么,当可以直接转换的时候为什么要费这个力气通过getAdapter()来转换呢?其实这种 机制可以让我们将目标类转换为它并没有实现的接口。举个例子,我们可能想把一个HashMap 当作List来用,尽管这两个类的性质并不相同,可以这么做:

IAdaptable adaptable = new HashMap();//译注:这里的HashMap应该不是指 java.util.HashMapList 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节点,这个节点的 格式如下:

  First String  Second String  Third String

因为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的类库

看自家总在期待,不知将来好歹,新乐吧总在不断等待,

IAdaptable是什么?

相关文章:

你感兴趣的文章:

标签云: