在Java中,有没有一种方法加载类实现不存在的接口?

时间:2011-09-12 08:45:21

标签: java interface classloader .class-file

我看过Java做了很多魔术,但有可能做到这一点:

在运行时,使用(例如)ClassLoader.defineClass,加载实现接口A的类B。接口B实际上并不存在于类路径中。 Java将抛出异常(ClassNotFoundException IIRC)并且不会加载该类。类A的所有其他部分都可以,我知道程序的其他任何部分都不会使用接口B。所以,我想要做的是让解释器忽略缺少接口定义并加载一个与A完全相同的类,除了没有实现接口{{1 }}

这可能吗?它当然可以通过捕获异常并手动编辑类B的二进制数据,然后再次加载它来实现。或者通过手动构建B.class文件然后加载它来在运行时创建名为B的虚拟空接口。但它看起来有点混乱,所以我的问题是,Java是否提供了任何方便的方法来实现这一目标?

如果没有,我想,我会尝试实施这两种方法中的一种,但我仍然想听听意见。

我这样做是为了提供一种方便的方式,让两个不同的代码库互相交互,如果它们都被加载,并且如果只有其中一个就可以正常工作。

3 个答案:

答案 0 :(得分:2)

我不知道任何非混乱的解决方案与你所描述的相似。这一切归结为自定义ClassLoader。自定义ClassLoaders很难开始,如果它们具有非常特定的语义,那么它们就会变得非常丑陋。

更不用说如果您的代码突然不在<{1}}内运行

,您将遇到的问题类型。

我认为理智的解决方案是生成ClassLoadermylibrary.jar(或更明确的:mylibrary-noB.jarmylibrary-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。您必须在与接口相同的类路径条目中定义的类中执行此操作。