Manish Kumar Sharma
Manish Kumar Sharma

Reputation: 13432

Reflectively create an anonymous class

I intend to import a library(jar) using my custom ClassLoader and it turns out the requirement is there to set a listener on one of the classes, like,

Class<?> clazz = getInitializerClass();
Object initializer = clazz.newInstance();

// I would like the below listener to be set on this initializer reflectively
// setOnInitListener() is mathod to be called on initializer
setOnInitListener(new OnInitCompleteListener(){
    @Override
    public void onInit(){
    }
}

The issue is that I don't have the listener available as a import and all I can get is a OnInitCompleteListener.class dynamically by classloading on the fly.

How can I achieve the above?

Note: I am willing to go any limits to achieve the same but a simple approach would be appreciated.

Upvotes: 1

Views: 71

Answers (2)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726589

Anonymous classes are compile-time artifacts. They even have names by which Java compiler can identify them, albeit inaccessible to programmers.

However, there is a work-around: since OnInitCompleteListener is an interface, you can produce a dynamic proxy that implements your interface:

InvocationHandler handler = new InvocationHandler() {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("onInit")) {
            ... // Listener's code goes here
        }
    }
};
Class<T> onInitCompleteListenerClass = ... // You get this one dynamically
Object listener = Proxy.newProxyInstance(
    classLoader,
    new Class[] { onInitCompleteListenerClass },
    handler);
// Use listener in the reflective call to a method taking OnInitCompleteListener

MyInvocationHandler implements InvocationHandler interface. Its invoke method can detect that it's being called in place of onInit, and perform the work as necessary.

Upvotes: 2

David
David

Reputation: 2702

Assuming your OnInitCompleteListener class is available in the same classloader you're running the rest of this code in...

You can get ahold of the setOnInitListener method with reflection against the class instance:

Method setter = clazz.getDeclaredMethod("setOnInitListener", OnInitCompleteListener.class);
setter.invoke(initializer, new OnInitCompleteListener() {
    @Override
    public void onInit()  {
        // implementation
    }
});

Upvotes: 0

Related Questions