21

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

Andrejs
  • 26,885
  • 12
  • 107
  • 96
  • 1
    Though as others mention IDE's may take some of the pain away, what you describe is not currently possible. I seem to remember a featurerequest/JSR proposing a syntax like 'MyObject#foo' or something, but can't find it anymore. – Arnout Engelen Mar 25 '12 at 21:52
  • Although its not possible I'm looking for possible workarounds that could currently be used – Andrejs Mar 25 '12 at 22:17
  • 2
    @ArnoutEngelen Java 8's lambdas are slated to have similar functionality: http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.html (section 8). Note that this won't give you a Method, but rather a lambda which you can then invoke. – yshavit Mar 25 '12 at 22:19
  • Some IDEs suggest changing strings which could refer to a class, method etc when you rename something. They can suggest changes in text files e.g. properties, and comment as well. This can be useful even if you just want to change what youc all something so that all the code is in sync. – Peter Lawrey Mar 26 '12 at 07:50
  • 1
    Possible duplicate of [How to get the MethodInfo of a Java 8 method reference?](https://stackoverflow.com/questions/19845213/how-to-get-the-methodinfo-of-a-java-8-method-reference) – Dean Xu Apr 24 '18 at 10:03

8 Answers8

13

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.

ddan
  • 379
  • 3
  • 4
10

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();
  }
}
Jim
  • 1,161
  • 9
  • 21
  • Are you suggesting to subclass the original MyObject and override foo() method to use this helper? – Andrejs Mar 25 '12 at 22:15
  • There is no need to subclass MyObject. Have foo() call **Method me = (new MethodNameHelper(){}).getMethod();** and save or use that result as you had wished. You hadn't mentioned how you wanted to **use** the **Method** object. – Jim Mar 25 '12 at 22:16
  • 1
    Fair enough. The thing is that foo() will already have some code in it that I'd rather not change or it might come from third party. Even so how would I return the Method me to the calling code (eg main). – Andrejs Mar 25 '12 at 22:22
  • Then I have misunderstood your use case. If you don't control the code of foo() then your code's connection to it is through the name and Reflection is your best bet. I had thought you wanted to write a foo() so that it or others could make use of its Method In that case the first call to foo() calculates its **Method** and stores it in a way needed by others (for example in a Map). – Jim Mar 25 '12 at 22:38
  • 4
    This technique is of limited value because you don't want to necessarily invoke the Method just to get its reference. Ideally they should be unrelated actions. It's unfortunately a flaw in Java's design that a lot of useful info that is known to the compiler isn't exposed until run-time (via reflection). – Magnus Aug 09 '12 at 03:29
  • 2
    `MethodNameHelper` should be abstract. – shmosel Jun 21 '16 at 02:37
9

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'
Jmini
  • 9,189
  • 2
  • 55
  • 77
Andrejs
  • 26,885
  • 12
  • 107
  • 96
  • 1
    Very cool, but it would be slightly cooler if they returned the `Method` instead of just its name. – shmosel Jun 21 '16 at 02:39
7

We have published the small library 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.

Benedikt Waldvogel
  • 12,406
  • 8
  • 49
  • 61
4

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.

Savvas Dalkitsis
  • 11,476
  • 16
  • 65
  • 104
1

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.

duffymo
  • 305,152
  • 44
  • 369
  • 561
  • By default Eclipse does not do the same thing for methods - only classes. I could use search but I won't always know that I need to look for String references – Andrejs Mar 25 '12 at 21:53
  • 1
    Like I said, IntelliJ knows how to do it. I'd recommend that you get a superior IDE. Eclipse ain't it. – duffymo Mar 25 '12 at 23:09
  • IntellJ unfortunately also can only use heuristics. It can for example happen that a method of the same name but different class gets renamed in a totally unrelated string. This is especially gruesome when you try to rename a method `getId` or `isActive`, which may exist on a lot of totally unrelated classes. – Svante Jul 27 '16 at 11:12
  • I have no trouble anything with ImtelliJ. – duffymo Jul 27 '16 at 18:37
-1

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.

Eugene Retunsky
  • 13,009
  • 4
  • 52
  • 55
-1

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.

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
  • Yes, I could, but that also would mean wrapping each method that needs to be used. It wouldn't be that convenient – Andrejs Mar 25 '12 at 21:49
  • I'd like to see an explanation of what led you to this design decision. I'm not convinced yet that reflection is necessary for what you're doing. This kind of design based on runtime lookup will be hard to maintain and debug going forward. – Paul Bellora Mar 25 '12 at 21:52
  • Added the PropertyChangeListener use case to the original question. – Andrejs Mar 25 '12 at 22:07