Как получить объект 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
.