Java Reflection: Call method from Interface name

21,484

Solution 1

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 };

Solution 2

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);
    }
}

Solution 3

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....
    }

}
Share:
21,484
Tapas Bose
Author by

Tapas Bose

Java developer.

Updated on September 23, 2020

Comments

  • Tapas Bose
    Tapas Bose over 3 years

    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.

  • Tapas Bose
    Tapas Bose over 11 years
    Thanks. So the implementation of the method AnyInvocationHandler#invoke is okay?
  • Vishy
    Vishy over 11 years
    @TapasBose The way I have re-written is, yes. ;)
  • Tapas Bose
    Tapas Bose over 11 years
    Thanks for the code snippet you have is working. But if I don't know the implementing class, here AnyThing, then how can I do it? Actually it is a demo app. In real one I don't know which class implemented it.
  • Vishy
    Vishy over 11 years
    You don't need to know what is implementing the interface, but you have to have a real instance. Otherwise you can't expect it to do anything.
  • Vishy
    Vishy over 11 years
    I have changed it so you can see how it can call an implementation of an interface without needing to know what it will be.
  • Tapas Bose
    Tapas Bose over 11 years
    Thanks, you example is helpful, but its not the real case. In real case I have only the interface name and I got it from RMI and I need to invoke a method of that interface.
  • Saintali
    Saintali over 11 years
    Are you telling me that you want to implement an interface without knowing what it is, and even without having a concrete implementation class around? Ask yourself what happens if you receive java.util.Collection from the RMI. Are you going to emulate an ArrayList or a HashSet or an LinkedBlockingQueue or....? Now there are thousands of these interfaces out there.