如何创建一个我无法更改的类,实现一个接口?
我有一个来自另一个闭源的库的类,但我希望能够使用它的接口。 原因是我不想在任何地方进行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
语句而变得缓慢而丑陋,但我必须写一个这些处理QuietFoo
和LoudFoo
上每个方法的方法。 扩展不是一个可行的解决方案,因为它违反了整个合同,因为LoudFoo
不是QuietFoo
。
基本上,给予Foo
:
public interface Foo { void bar(); }
如何在不更改源的情况下使QuietFoo
实现Foo
,这样我就不必在代码中的任何地方进行转换和instanceof
调用?
有两种方法:
- 使用适配器模式
- 使用
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个部分:
- 界面(在这种情况下,
Foo
) -
InvocationHandler
- 创建代理(
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();
这种方法需要比实现单个适配器更多的代码,但是它足够通用,可以用于为任何类和接口对创建自动适配器,而不仅仅是QuietFoo
和Foo
示例。 当然,这个方法使用reflection( Proxy
类使用reflection,我们的InvocationHandler
),这可能会更慢,但JVM的最近改进使得reflection比以前快得多。