我看过Java做了很多魔术,但有可能做到这一点:
在运行时,使用(例如)ClassLoader.defineClass
,加载实现接口A
的类B
。接口B
实际上并不存在于类路径中。 Java将抛出异常(ClassNotFoundException
IIRC)并且不会加载该类。类A
的所有其他部分都可以,我知道程序的其他任何部分都不会使用接口B
。所以,我想要做的是让解释器忽略缺少接口定义并加载一个与A
完全相同的类,除了没有实现接口{{1 }}
这可能吗?它当然可以通过捕获异常并手动编辑类B
的二进制数据,然后再次加载它来实现。或者通过手动构建B.class文件然后加载它来在运行时创建名为B的虚拟空接口。但它看起来有点混乱,所以我的问题是,Java是否提供了任何方便的方法来实现这一目标?
如果没有,我想,我会尝试实施这两种方法中的一种,但我仍然想听听意见。
我这样做是为了提供一种方便的方式,让两个不同的代码库互相交互,如果它们都被加载,并且如果只有其中一个就可以正常工作。
答案 0 :(得分:2)
我不知道任何非混乱的解决方案与你所描述的相似。这一切归结为自定义ClassLoader
。自定义ClassLoaders
很难开始,如果它们具有非常特定的语义,那么它们就会变得非常丑陋。
更不用说如果您的代码突然不在<{1}}内运行
,您将遇到的问题类型。我认为理智的解决方案是生成ClassLoader
和mylibrary.jar
(或更明确的:mylibrary-noB.jar
和mylibrary-withB.jar
并让用户只选择他们想要的那个。
答案 1 :(得分:1)
包含带有附加接口的第二个jar是相当常见的。如果需要,可以包括第二个罐子,否则丢弃。如果将其添加到类路径的末尾,则会隐式发生。
答案 2 :(得分:0)
最终,定义接口B
的类路径条目中的代码必须使用与定义类A
的类路径条目中的代码相同的类。因此,定义虚拟接口将不起作用。
在Java中,您可以使用代理动态实现接口:
http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html
因此,在稍后的时间点,当您的接口A
可用时,您可以通过反射获取它并创建实现其方法的代理。以下示例将java.lang.Runnable
接口显示为接口A
:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) throws Exception {
Class interfaceClass = Class.forName("java.lang.Runnable");
Object implementingRunnable = Proxy.newProxyInstance(
ProxyTest.class.getClassLoader(),
new Class[] {interfaceClass},
new MyInvocationHandler()
);
((Runnable)implementingRunnable).run();
}
static class MyInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
System.out.println("called " + m);
return null;
}
}
}
当然,在某些时候你必须将代理实际转换为接口A
。您必须在与接口相同的类路径条目中定义的类中执行此操作。