Как получить объект Method в Java без использования имен строк метода

Я ищу удобное обходное решение для получения объекта Method из метода. Идея:

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

Очевидным способом является использование имени метода в виде строки:

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

но я хочу избежать этого, потому что, если я переименую foo(), этот код перестанет работать или я переименую строку во всех местах, где она используется.

Вариант использования заключается в том, что я хочу использовать что-то похожее на ProperyChangeListeners, однако они полагаются на имя метода как строку. Я бы хотел использовать фактический метод (безопасно) и не полагаться на строки.

Что я могу использовать, чтобы получить метод безопасным способом переименования?

UPDATE: Я хотел бы найти чистое Java-решение, которое не полагается на функции IDE.

Ответ 1

На самом деле есть библиотека, которая может сделать это:

Jodd MethRef - строго типизированные ссылки на имена методов

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'

Ответ 2

Ссылки на методы Java 8 идеально подходят для этого - сложная часть переходит к базовому методу, поскольку сам синтаксис метода приводит к непрозрачному лямбда-объекту.

Обнаружено это после небольшого поиска:

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

Аккуратный трюк - вызов метода на прокси-объекте, который записывает имя метода. Не пробовал, но выглядит многообещающе.

Ответ 3

В вызове метода: Метод me = (новый 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();
  }
}

Ответ 4

Мы опубликовали небольшую библиотеку de.cronn: reflection-util, которая может использоваться для захвата метода.

Пример:

class MyClass {

    public void myMethod() {
    }

}

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

Подробности реализации: Подкласс подпрограммы MyClass создается с помощью ByteBuddy, и вызов метода захватывается для извлечения его имя. ClassUtils кэширует информацию таким образом, что нам не нужно создавать новый прокси-сервер при каждом вызове.

Ответ 5

Самое безопасное - использовать IDE с возможностями рефакторинга, достаточно умными, чтобы распознавать и заменять это имя метода String. IntelliJ из JetBrains делает это - я просто доказал это сам.

Ответ 6

Возможно, вы захотите использовать аннотации. Вы можете создать пользовательскую аннотацию для каждого метода, который вы хотите получить, и использование отражения вытащите этот метод из объекта.

Вы можете безопасно переименовать метод, и ваш код будет работать.

Если вы хотите получить множество методов, используйте только одну аннотацию со строковым значением, которое будет изменено для каждого метода, а затем код отражения будет искать эти аннотации с этим строковым значением. Вы все равно можете переименовать свой метод, пока вы оставите строковое значение аннотации тем же.

Ответ 7

Вы можете использовать рефакторинг IDE, который также заботится обо всех вводах строк.

Вы не можете ссылаться на метод с использованием отражения, иначе как на его имя.

Ответ 8

Используя рефлексию, вы отказываетесь от безопасности во время компиляции, просто по своей природе.

Вместо этого подумайте, действительно ли здесь необходимо отражение. Вы можете представить свой метод с помощью Runnable, например:

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

...

foo.run();

Или, если вам нужно представить метод с возвращаемым типом, посмотрите Callable - или даже лучше, Guava Function.