Tavian Barnes
Tavian Barnes

Reputation: 12922

How can I copy a java.lang.reflect.Method?

I have an API that takes in a Method and stores it to call later. In order to invoke it I have to do setAccessible(true). Before I do that, I'd like to make a defensive copy. But how?

I thought of

method.getDeclaringClass()
      .getDeclaredMethod(method.getName(), method.getParameterTypes());

but that won't necessarily give me the same method back in the presence of bridge methods (or other cases where two methods have the same name/parameter types but different return types).

I could loop over method.getDeclaringClass().getDeclaredMethod() and look for an exact match, but that seems inefficient.


An example that illustrates why a defensive copy might be nice:

Method method = ...;

// Does setAccessible(true)
MyInvoker invoker = new MyInvoker(method);

// Sometime later, the user uses the same Method rather than re-retrieving it
method.setAccessible(true);
method.invoke(...);
method.setAccessible(false);
// Oops, now MyInvoker is broken

An example where getDeclaredMethod() returns a different method:

interface Iface {
    Object get();
}

class Impl implements Iface {
    @Override
    public String get() {
        return "foo";
    }
}

for (Method method : Impl.class.getDeclaredMethods()) {
    System.out.println(method);
    System.out.println(copy(method));
    System.out.println();
}

private Method copy(Method method) {
    try {
        return method.getDeclaringClass()
                .getDeclaredMethod(method.getName(), method.getParameterTypes());
    } catch (ReflectiveOperationException e) {
        throw new RuntimeException(e);
    }
}

For me, this prints:

public java.lang.String com.maluuba.api.Impl.get()
public java.lang.String com.maluuba.api.Impl.get()

public java.lang.Object com.maluuba.api.Impl.get()
public java.lang.String com.maluuba.api.Impl.get()

Upvotes: 1

Views: 277

Answers (1)

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279940

You'll have to do what you originally suggested and rely on the contract of Method#equals(Object) which states

Compares this Method against the specified object. Returns true if the objects are the same. Two Methods are the same if they were declared by the same class and have the same name and formal parameter types and return type.

So you won't be able to use the getDeclaredMethod(String, Object...) method. You'll have to do an array lookup on the Method[] returned by getDeclaredMethods(). For example,

private Method copy(Method method) {
    Class<?> clazz = method.getDeclaringClass();
    for (Method declaredMethod : clazz.getDeclaredMethods()) {
        if (declaredMethod.equals(method)) {
            return declaredMethod; // return the new one
        }
    }
    throw new RuntimeException("This should not happen.");
}

Upvotes: 1

Related Questions