How to get Method object in Java without using met

2020-02-19 08:40发布

问题:

I'm looking for a convenient workaround for getting the Method object from a method. The idea:

Method fooMethod = getMethod( new MyObject().foo() ) // returns method "foo" in MyObject

The obvious way is to use the name of the method as a string:

Method fooMethod = MyObject.class.getMethod("foo")

but I want to avoid this because if I rename foo() that code will stop working or I have rename the string in all the places where it is used.

The use case is that I want to use something similar to ProperyChangeListeners however those rely on the method name as string. I'd like to use the actual method (safely) and not rely on strings.

What could I use to get the method in a rename safe way?

UPDATE: I'd like to find a pure java solution that does not rely on IDE features

回答1:

Java 8 method references would be ideal for this - the tricky part is getting to the underlying method, because the method reference syntax itself results in an opaque lambda object.

Found this after a bit of searching:

http://benjiweber.co.uk/blog/2013/12/28/typesafe-database-interaction-with-java-8/

Neat trick - call the method on a proxy object that records the method name. Haven't tried it but looks promising.



回答2:

In your method call: Method me = (new MethodNameHelper(){}).getMethod();

/**
 * Proper use of this class is
 *     Method me = (new MethodNameHelper(){}).getMethod();
 * the anonymous class allows easy access to the method name of the enclosing scope.
 */
public class MethodNameHelper {
  public Method getMethod() {
    return this.getClass().getEnclosingMethod();
  }
}


回答3:

There is actually a library that can do this:

Jodd MethRef - Strongly-typed method name references

https://jodd.org/ref/methref.html

Methref<Str> m = Methref.on(Str.class);  // create Methref tool

// example #1
m.to().boo();
m.ref();                               // returns String: 'boo'


回答4:

We have published the small library de.cronn:reflection-util that can be used to capture a method.

Example:

class MyClass {

    public void myMethod() {
    }

}

Method method = ClassUtils.getVoidMethod(MyClass.class, MyClass::myMethod);
System.out.println(method.getName()); // prints "myMethod"

Implementation details: A Proxy subclass of MyClass is created with ByteBuddy and a call to the method is captured to retrieve its name. ClassUtils caches the information such that we do not need to create a new proxy on every invocation.



回答5:

You might want to use annotations. You can create a custom annotation for each method that you want to retrieve and the using reflection pull that method from the object.

You can safely rename the method and your code will work.

If you want to be able to retrieve many methods then use only one annotation with a string value which will change per method and then your reflection code will look for those annotations with that string value. You can still rename your method as long as you leave the string value of the annotation the same.



回答6:

The safest thing is to use an IDE with refactoring capability that's smart enough to recognize and replace that String method name. IntelliJ from JetBrains does it - I just proved it to myself.



回答7:

You can use IDE refactoring which take care about all string occurences as well.

You cannot reference a method using reflection otherwise than by its name.



回答8:

By using reflection you're giving up compile-time safety, just by its nature.

Instead consider whether reflection is really necessary here. You could represent your method with a Runnable, for example:

Runnable foo = new Runnable() {
    @Override
    public void run() {
        new MyObject().foo();
    }
}

...

foo.run();

Or if you need to represent a method with a return type, look at Callable - or even better, Guava's Function.