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的最近改进使得反射比以前快得多. (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
