Tapas Bose
Tapas Bose

Reputation: 29816

Java Reflection: Call method from Interface name

I have have a name of an interface and I want to invoke a method defined by its concrete implemented class. So I took help of Java Reflection.

The interface:

package tsb.learning.reflection;

public interface IAnyThing {

    void doSomething();
}

It's implemented class:

package tsb.learning.reflection;

public class AnyThing implements IAnyThing {

    public void doSomething() {
        System.out.println("JYM");
    }
}

The implementation of InvocationHandler:

package tsb.learning.reflection;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class AnyInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(proxy, args);
    }
}

And the Controller:

package tsb.learning.reflection;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Controller {

    /**
     * @param args
     * @throws ClassNotFoundException
     */
    public static void main(String[] args) throws ClassNotFoundException {
        String interfaceName = "tsb.learning.reflection.IAnyThing";
        ClassLoader classLoader = Class.forName(interfaceName).getClassLoader();
        Class<?>[] interfaces = new Class<?>[] { Class.forName(interfaceName) };
        InvocationHandler handler = new AnyInvocationHandler();
        IAnyThing anyThing = (IAnyThing) Proxy.newProxyInstance(classLoader, interfaces, handler);
        anyThing.doSomething();
    }
}

But it is not working and I am getting the following exception:

Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at tsb.learning.reflection.AnyInvocationHandler.invoke(AnyInvocationHandler.java:10)
    at $Proxy0.doSomething(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at tsb.learning.reflection.AnyInvocationHandler.invoke(AnyInvocationHandler.java:10)
    at $Proxy0.doSomething(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at tsb.learning.reflection.AnyInvocationHandler.invoke(AnyInvocationHandler.java:10)
    at $Proxy0.doSomething(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at tsb.learning.reflection.AnyInvocationHandler.invoke(AnyInvocationHandler.java:10)

The exception is printing in console in loop, I need to stop the program.

Any information will be very helpful to me.

Upvotes: 3

Views: 24452

Answers (3)

Saintali
Saintali

Reputation: 4571

The only method in IAnyThing is doSomething(), so I guess in the InvocationHandler you know what the method is. Just put your implementation there. Also, beside doSomething() you should also handle three methods inherited from java.lang.Object:

public static class AnyInvocationHandler implements InvocationHandler {

    private static final Method doSomething;

    static {
        try {
            doSomething = IAnyThing.class.getMethod("doSomething");
        } catch (NoSuchMethodException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getDeclaringClass() == Object.class)
            return handleObjectMethod(proxy, method, args);

        if (doSomething.equals(method)) {
            doSomethingImpl();
            return null;
        }

        throw new AbstractMethodError(method.toString());
    }

    private Object handleObjectMethod(Object proxy, Method method, Object[] args) {
        switch (method.getName()) {
            case "equals":
                return proxy == args[0];
            case "hashCode":
                return System.identityHashCode(proxy);
            case "toString":
                return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy));
            default:
                throw new AssertionError();
        }
    }

    private void doSomethingImpl() {
        // implement....
    }

}

Upvotes: 1

mbelow
mbelow

Reputation: 1133

Inside your AnyInvocationHandler, you could delegate the call to an instance of your AnyThing:

public class AnyInvocationHandler implements InvocationHandler {

    private AnyThing delegate = new AnyThing();

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // to something useful here
        [...]
        // finally, invoke the method on implementation class.
        return method.invoke(delegate, args);
    }
}

Upvotes: 1

Peter Lawrey
Peter Lawrey

Reputation: 533920

Caused by: java.lang.reflect.InvocationTargetException

This mean the method you called threw an exception. You need to look at the exception which appears after it and cause this one. It has nothing to do with how you called the method.

I suspect you are getting a StackOverflowError

// calls the same method on the same proxy which will recurse until you get an error.
return method.invoke(proxy, args);

Instead try calling a method on a real object to do something.

public class AnyInvocationHandler implements InvocationHandler {
    final IAnyThing iat;

    public AnyInvocationHandler(IAnyThing iat) {
        this.iat = iat;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // call the same method on a real object.
        return method.invoke(iat, args);
    }
}

BTW You can write

Class interfaceClass = tsb.learning.reflection.IAnyThing.class;
ClassLoader classLoader = interfaceClass.getClassLoader();
Class<?>[] interfaces = new Class<?>[] { interfaceClass };

Upvotes: 4

Related Questions