如何创建一个我无法更改的类,实现一个接口?

我有一个来自另一个闭源的库的类,但我希望能够使用它的接口。 原因是我不想在任何地方进行instanceof检查或null -checks,但我也不想扩展现有的类。

例如,假设我有这个代码:

 public class Example { // QuietFoo is from another library that I can't change private static QuietFoo quietFoo; // LoudFoo is my own code and is meant to replace QuietFoo private static LoudFoo loudFoo; public static void main(String[] args) { handle(foo); } private static void handle(Object foo) { if (foo instanceof QuietFoo) ((QuietFoo) foo).bar(); else if (foo instanceof LoudFoo) ((LoudFoo) foo).bar(); } } 

我不能改变QuietFoo

 public class QuietFoo { public void bar() { System.out.println("bar"); } } 

但我可以改变LoudFoo

 public class LoudFoo { public void bar() { System.out.println("BAR!!"); } } 

问题是,在许多类中可能还有许多其他的bar实现,并且可能有更多的方法而不仅仅是bar ,所以不仅我的handle方法会因为大量的instanceof语句而变得缓慢而丑陋,但我必须写一个这些处理QuietFooLoudFoo上每个方法的方法。 扩展不是一个可行的解决方案,因为它违反了整个合同,因为LoudFoo不是QuietFoo

基本上,给予Foo

 public interface Foo { void bar(); } 

如何在不更改源的情况下使QuietFoo实现Foo ,这样我就不必在代码中的任何地方进行转换和instanceof调用?

有两种方法:

  1. 使用适配器模式
  2. 使用Proxy

适配器方法将更简单但不太灵活,并且Proxy方法将更复杂但更灵活。 即使Proxy方法更复杂,但这种复杂性仅限于几个类。


适配器

适配器模式很简单。 对于您的示例,它只是一个类,如下所示:

 public class QuietFooAdapter implements Foo { private QuietFoo quietFoo; public QuietFooAdapter(QuietFoo quietFoo) { this.quietFoo = quietFoo; } public void bar() { quietFoo.bar(); } } 

然后使用它:

 Foo foo = new QuietFooAdapter(new QuietFoo()); foo.bar(); 

这很好,但是如果你有一个以上的类来制作适配器,这可能很乏味,因为你需要为你必须包装的每个类都有一个新的适配器。


Java的Proxy

Proxy是一个本机Java类,它是reflection库的一部分,允许您创建更通用的reflection解决方案。 它涉及3个部分:

  1. 界面(在这种情况下, Foo
  2. InvocationHandler
  3. 创建代理( Proxy.newProxyInstance

我们已经有了界面,所以我们在那里很好。

InvocationHandler是我们通过reflection“自动适应”的地方:

 public class AdapterInvocationHandler implements InvocationHandler { private Object target; private Class targetClass; public AdapterInvocationHandler(Object target) { this.target = target; targetClass = target.getClass(); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Method targetMethod = targetClass.getMethod(method.getName(), method.getParameterTypes()); if (!method.getReturnType().isAssignableFrom(targetMethod.getReturnType())) throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not support: " + method.toGenericString()); return targetMethod.invoke(target, args); } catch (NoSuchMethodException ex) { throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not support: " + method.toGenericString()); } catch (IllegalAccessException ex) { throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not declare method to be public: " + method.toGenericString()); } catch (InvocationTargetException ex) { // May throw a NullPointerException if there is no target exception throw ex.getTargetException(); } } } 

这里重要的代码在try块中。 这将处理将代理上调用的任何方法调用适配到内部target对象的过程。 如果在不支持的接口上调用方法(非public ,错误的返回类型,或者只是不存在),则抛出UnsupportedOperationException 。 如果我们捕获InvocationTargetException ,我们将通过InvocationTargetException.getTargetException重新抛出导致它的exception。 当我们调用的方法reflection抛出exception时会发生这种情况。 Java将其包装在一个新的exception中并抛出该新exception。

接下来,我们需要一些东西来创建适配器:

 public class AdapterFactory { public static  T createAdapter(Object target, Class interfaceClass) { if (!interfaceClass.isInterface()) throw new IllegalArgumentException("Must be an interface: " + interfaceClass.getName()); return (T) Proxy.newProxyInstance(null, new Class[] { interfaceClass }, new AdapterInvocationHandler(target)); } } 

如果愿意,您还可以将AdapterInvocationHandler类嵌套在AdapterFactory类中,以便AdapterFactory中的所有内容都是自包含的。

然后使用它:

 Foo foo = AdapterFactory.createAdapter(new QuietFoo(), Foo.class); foo.bar(); 

这种方法需要比实现单个适配器更多的代码,但是它足够通用,可以用于为任何类和接口对创建自动适配器,而不仅仅是QuietFooFoo示例。 当然,这个方法使用reflection( Proxy类使用reflection,我们的InvocationHandler ),这可能会更慢,但JVM的最近改进使得reflection比以前快得多。