Комментарии 12
import sun.misc.SharedSecrets;
import sun.reflect.ConstantPool;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.function.Function;
public class MethodRefs {
public static void main(String[] args) throws Exception {
Function<Object, Integer> hashCodeRef = Object::hashCode;
System.out.println(unreference(hashCodeRef));
}
public static Method unreference(Function<?, ?> ref) {
ConstantPool pool = SharedSecrets.getJavaLangAccess().getConstantPool(ref.getClass());
int size = pool.getSize();
for (int i = 1; i < size; i++) {
try {
Member member = pool.getMethodAt(i);
if (member instanceof Method) {
return (Method) member;
}
} catch (IllegalArgumentException e) {
// skip non-method entry
}
}
throw new IllegalArgumentException("Not a method reference");
}
}
А подход с пулом костант хорош!
Правда у меня есть одно замечание — брать первый попавшийся метод некорректно, там может быть что-угодно (например java.lang.Integer.valueOf(int), используемый для боксинга). Во всяком случае мне неизвестно, всегда ли нужный метод будет встречаться первым. Нужен дополнительный анализ
Неплохой вариант, но, к сожалению не работает начиная с Java 9. Развернутый комментарий написал в статье
А где такие хаки могут понадобиться?
Боюсь даже предположить. Вообще было бы удобно иметь такой универсальный инструмент, когда тебе в коде нужно получить конкретный метод конкретного класса.
getDeclaredMethod это конечно хорошо, но не так синтаксически красиво, как явная ссылка на метод через ::
Иногда есть смысл сослаться статично на метод, известный при компиляции. Потом будет легко переименовать метод, и не поломать случайно код:
class SimpleWebFramework {
public static String notFound() {
return "Page not found!";
}
public String invokeMethodForPath(Map<String, Method> methods, String path) {
Method m = methods.getOrDefault(path, unreference(SimpleWebFramework::notFound));
....
return String.valueOf(m.invoke(....));
....
}
}
Прикольно, но очень не нравится что делается вызов исходного метода. Если нам не повезет и он реально будет принимать Object, то мы на вызове unreference сначала сотворим side effect, а потом еще и замаскируем его с помощью RuntimeException("Something's wrong").
Всё верно, для методов Object этот код совершенно не годится.
Я про другое. С методами класса Object просто вылетит ексепшн мол "я такое не умею" и все будет хорошо. А вот если у меня есть статический метод, который принимает Object, то внутри он может делать какой-нибудь side effect. И вызов unreference этот side effect успешно после себя оставит.
Понятно, что это очень теоретическая проблема и статических методов с сайд эффектом, принимающих Object, не существует. Но, все-таки, в библиотечной функции такая, даже сугубо теоретическая, проблема недопустима.
p.s. Но повторюсь. Решение прикольное (в хорошем смысле этого слова).
Преобразование Method Reference в Method в языке Java