In Java, is there a way load class implementing interface that does not exist?
I've seen Java doing a lot of magic, but is it possible to do this:
At runtime, using (for example) ClassLoader.defineClass
, load class A
that implements interface B
. The interface B
does not actually exist in class path. Java will throw an exception (ClassNotFoundException
IIRC) and the class won't be loaded. All other parts of class A
are OK, and I know for a fact that no other part of progra开发者_StackOverflow中文版m will be using interface B
. So, what I want to do is to make the interpreter ignore the missing interface definition and load a class that is completely the same as A
except that is does not implement the interface B
.
Is this possible? It of course could be achieved by catching the exception and manually editing the binary data of class A
, then loading it again. Or by creating a dummy empty interface named B at runtime by manually constructing the B.class file and then loading it. But it seems a bit messy, so my question is, does Java provide any convenient ways to do this?
If not, I guess, I'll try implementing one of those two methods, but I still want to hear an opinion.
I'm doing this to provide a convenient way for two different code bases to interact with each other if they both are loaded, and to just work fine if only one of them is.
I'm not aware of any non-messy solutions to this that work similarly to what you're describing. It all boils down to a custom ClassLoader
. Custom ClassLoaders
are kind-of hard to begin with and if they have very specific semantics like this, then they become very ugly.
Not to mention the types of problems that you'll run into if your code suddenly doesn't run inside that ClassLoader
.
I think the sane solution to this is to produce a mylibrary.jar
and a mylibrary-noB.jar
(or even more explicit: mylibrary-withB.jar
and mylibrary-noB.jar
) and let the users just select which one they want.
It is fairly common to include a second jar with the additional interfaces. The second jar can be included if needed and dropped if not. If its added to the end of the class path, this will happen implicitly.
Ultimately, the code in the classpath entry where interface B
is defined has to use the same class as the code in the classpath entry where class A
is defined. So defining a dummy interface will not work.
In Java you can dynamically implement an interface with a Proxy:
http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html
So at a later point in time, when your interface A
is available, you can get it by reflection and create Proxy that implements its methods. The following example shows this with the java.lang.Runnable
interface as interface 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;
}
}
}
Of course at some point you have to actually cast the proxy to the interface A
. You would have to do this in a class that is defined in the same class path entry as the interface.
精彩评论