Как получить ссылку на метод для всех методов в классе (Java)?

Справочник по методу для конкретного метода в Java 8 можно получить как Class::Method. Но как получить ссылку на метод всех методов класса?

Все нужные методы имеют разные имена, но одинаковую сигнатуру типа. Кроме того, имена методов неизвестны заранее.

Пример:

class Test {
    public static double op0(double a) { ... }
    public static double op1(double a) { ... }
    public static double op2(double a) { ... }
    public static double op3(double a) { ... }
    public static double op4(double a) { ... }
}

Ссылку на известный метод op0 можно получить так:

DoubleFunction<Double> f = Test::op0;

Но как получить ссылки на все методы класса?


person wolfram77    schedule 30.03.2015    source источник
comment
Вы имеете в виду, как получить их в помощнике по содержимому IDE?   -  person Konstantin Yovkov    schedule 30.03.2015
comment
Нет, чтобы получить ссылки на методы, чтобы их можно было вызвать в более поздний момент времени. Это возможно с помощью Reflection, но я искал более легкую альтернативу.   -  person wolfram77    schedule 30.03.2015
comment
Также неверно утверждение нужные методы имеют разные имена функций, но одинаковую сигнатуру функции. Сигнатура включает имя метода и список параметров. Тип возвращаемого значения не является частью сигнатуры метода.   -  person Konstantin Yovkov    schedule 30.03.2015
comment
Извините моя ошибка. Он должен называться Type Signature.   -  person wolfram77    schedule 30.03.2015


Ответы (1)


Не существует решения, работающего без рефлексии, поскольку динамическое обнаружение существующих методов является рефлексивной операцией. Однако после обнаружения методов и создания экземпляра ссылки на метод (или его динамического эквивалента) фактический вызов кода выполняется без отражения:

class Test {
    public static double op0(double a) { ... }
    public static double op1(double a) { ... }
    public static double op2(double a) { ... }
    public static double op3(double a) { ... }
    public static double op4(double a) { ... }

    static final Map<String, DoubleUnaryOperator> OPS;
    static {
        HashMap<String, DoubleUnaryOperator> map=new HashMap<>();
        MethodType type=MethodType.methodType(double.class, double.class);
        MethodType inT=MethodType.methodType(DoubleUnaryOperator.class);
        MethodHandles.Lookup l=MethodHandles.lookup();
        for(Method m:Test.class.getDeclaredMethods()) try {
          if(!Modifier.isStatic(m.getModifiers())) continue;
          MethodHandle mh=l.unreflect(m);
          if(!mh.type().equals(type)) continue;
          map.put(m.getName(), (DoubleUnaryOperator)LambdaMetafactory.metafactory(
            l, "applyAsDouble", inT, type, mh, type).getTarget().invokeExact());
        } catch(Throwable ex) {
            throw new ExceptionInInitializerError(ex);
        }
        OPS=Collections.unmodifiableMap(map);
    }
}

После того, как класс был инициализирован, вы можете вызвать конкретную операцию без отражения, используя OPS.get(name).applyAsDouble(doubleValue), или вызвать все операции, используя, например,

OPS.forEach((name,op)-> System.out.println(name+'('+42+") => "+op.applyAsDouble(42)));
person Holger    schedule 07.04.2015
comment
Спасибо @Holger. Я уже нашел ваши ответы здесь и здесь, который решил мою проблему. Это помогает мне делать то же самое с методами экземпляра. Это действительно очень быстро. - person wolfram77; 08.04.2015
comment
почему вы использовали DoubleUnaryOperator вместо DoubleFunction? и как "applyAsDouble" имени метода достаточно без указания имени класса или интерфейса? - person wolfram77; 08.04.2015
comment
DoubleFunction — это функция, которая принимает double и возвращает Object; DoubleUnaryOperator потребляет double и возвращает double, поэтому идеально соответствует сигнатуре методов в вашем примере. Требуемый interface получается из возвращаемого типа инструкции invokedynamic; в нашем рефлексивном использовании оно появляется дважды. Параметр invokedType (переменная inT) указывает тип возвращаемого значения, а наш вызов invokeExact() является полиморфным по сигнатуре и использует преобразование типа (DoubleUnaryOperator) для определения типа возвращаемого значения, которое, конечно же, должно совпадать с inT. - person Holger; 08.04.2015
comment
Привет, а что, если бы методы op не были бы статическими, поэтому ссылайтесь на метод экземпляра произвольного объекта определенного типа. Как тогда получить такую ​​карту? - person Kristoff; 04.05.2020
comment
@ Кристофф, ты должен решить. Хотите ли вы связать (захватить) конкретный экземпляр во время создания, чтобы он всегда использовался при оценке DoubleUnaryOperator, или вы хотите передавать экземпляр (потенциально каждый раз разный) каждый раз, когда вы оцениваете функцию. этот ответ обсуждает оба варианта на одном примере. Однако во втором случае вам понадобится собственный интерфейс для потребления объекта и double и создания double (если вы не согласны с боксом). - person Holger; 04.05.2020