Java 如何创建一个我可以';不改变,实现一个接口?
我有一个来自另一个封闭源代码库的类,但我希望能够为它使用接口。原因是我不想执行Java 如何创建一个我可以';不改变,实现一个接口?,java,interface,adapter,instanceof,proxy-classes,Java,Interface,Adapter,Instanceof,Proxy Classes,我有一个来自另一个封闭源代码库的类,但我希望能够为它使用接口。原因是我不想执行instanceof检查或null-到处检查,但我也不想扩展现有的类 例如,假设我有以下代码: public class Example { // QuietFoo is from another library that I can't change private static QuietFoo quietFoo; // LoudFoo is my own code and is mean
instanceof
检查或null
-到处检查,但我也不想扩展现有的类
例如,假设我有以下代码:
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");
}
}
public class LoudFoo {
public void bar() {
System.out.println("BAR!!");
}
}
public interface Foo {
void bar();
}
但是我可以改变LoudFoo
public class QuietFoo {
public void bar() {
System.out.println("bar");
}
}
public class LoudFoo {
public void bar() {
System.out.println("BAR!!");
}
}
public interface Foo {
void bar();
}
问题是,在许多类中可能有许多其他的bar
实现,并且可能有比bar
更多的方法,因此我的处理
方法不仅会变得缓慢和丑陋,而且会有很多语句的实例,但是我必须为QuietFoo
和LoudFoo
上的每个方法编写一个句柄方法。扩展不是一个可行的解决方案,因为它违反了整个is-a契约,因为LoudFoo
不是QuietFoo
基本上,给定Foo
:
public class QuietFoo {
public void bar() {
System.out.println("bar");
}
}
public class LoudFoo {
public void bar() {
System.out.println("BAR!!");
}
}
public interface Foo {
void bar();
}
如何在不更改源代码的情况下使QuietFoo
实现Foo
,这样我就不必在代码中的任何地方执行强制转换和instanceof
调用?有两种方法:
使用适配器模式
使用
适配器方法将更简单,但灵活性较差,代理方法将更复杂,但更灵活。尽管代理方法更为复杂,但这种复杂性仅限于几个类
适配器
问题很简单。例如,它将只是一个类,如下所示:
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();
Foo foo = AdapterFactory.createAdapter(new QuietFoo(), Foo.class);
foo.bar();
这很好,但是如果要为多个类创建适配器,这可能会很乏味,因为您需要为每个必须包装的类创建一个新的适配器
Java的Proxy
Class
Proxy
是一个本机Java类,它是反射库的一部分,允许您创建更通用的反射解决方案。它包括三个部分:
接口(在本例中,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();
}
}
}
如果愿意,您还可以将adapterHolidationHandler
类嵌套在AdapterFactory
类中,以便所有内容都包含在AdapterFactory
中
然后使用它:
Foo foo = new QuietFooAdapter(new QuietFoo());
foo.bar();
Foo foo = AdapterFactory.createAdapter(new QuietFoo(), Foo.class);
foo.bar();
这种方法比实现单个适配器需要更多的代码,但它具有足够的通用性,可以用于为任何类和接口对创建自动适配器,而不仅仅是QuietFoo
和Foo
示例。诚然,这种方法使用反射(代理类使用反射,我们的调用处理程序也使用反射),这可能会慢一些,但JVM最近的改进使反射比以前快得多。在有人批评我之前:@Gamb阅读我评论中的那篇博文--如果你把鼠标移到“x分钟前”,你会看到,从技术上讲,时间是完全相同的:3Ah,好吧。当你发布一个问题时,你可以选择“回答你自己的问题”(底部的复选框),这也会给你一个输入答案的位置。这就是我如何让它在同一时间提交的。我使用了“你”“这样人们就可以用对话的方式来看待它,让它更容易阅读。我知道这一点。它将其转换为wiki条目,而不是问题答案(IMHO)。不用担心,这是一个很酷的答案,但我的评论似乎属于meta的问题,而不是这里:)@Gamb如果你问一个关于meta的问题,让我知道你发现了什么。+1有趣的反射方法(这里是反射爱好者)。