java – 如何创建一个我无法更改的类,实现一个接口?
我有一个来自另一个闭源的库的类,但我希望能够使用它的接口.原因是我不想在任何地方进行instanceof检查或空检查,但我也不想扩展现有的类.
例如,假设我有这样的代码: 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调用? 解决方法
有两种方法:
>使用适配器模式 适配器方法将更简单但灵活性更低,并且代理方法将更复杂但更灵活.尽管代理方法更复杂,但这种复杂性仅限于几个类. 适配器 adapter pattern很简单.对于您的示例,它只是一个类,如下所示: 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是一个本机Java类,它是反射库的一部分,允许您创建更通用的反射解决方案.它涉及3个部分: >界面(在这种情况下,Foo) 我们已经有了界面,所以我们在那里很好. InvocationHandler是我们通过反射“自动适应”的地方: 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块中.这将处理将代理上调用的任何方法调用适配到内部目标对象的过程.如果在不支持的接口上调用了一个方法(非公共,错误的返回类型,或者只是不存在),那么我们抛出UnsupportedOperationException.如果我们捕获InvocationTargetException,我们将通过InvocationTargetException.getTargetException重新抛出导致它的异常.当我们调用的方法反射抛出异常时会发生这种情况. Java将其包装在一个新的异常中并抛出该新异常. 接下来,我们需要一些东西来创建适配器: public class AdapterFactory { public static <T> T createAdapter(Object target,Class<T> 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示例.当然,这种方法使用反射(Proxy类使用反射,我们的InvocationHandler也是如此),这可能会更慢,但JVM的最近改进使得反射比以前快得多. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |