Передать ссылку на метод из разных подклассов

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

User user = new User(...);
user.getValue(ADerived::getPropertyFromA);
user.getValue(BDerived::getPropertyFromB);

ADerived и BDerive выглядят следующим образом (упрощенно):

public abstract class Base { }

public class ADerived extends Base {
    private static final String A_OUTPUT = "Test A";

    public String getPropertyFromA() {
        return A_OUTPUT;
    }
}

public class BDerived extends Base {
    private static final String B_OUTPUT = "Test B";

    public String getPropertyFromB() {
        return B_OUTPUT;
    }
}

Метод getPropertyFromX специально не определен в классе Base, поскольку они сильно различаются.

В классе BaseUser я сохраняю экземпляр подклассов на карте и пытаюсь выполнить переданный метод для этого экземпляра. По крайней мере, этот код компилируется, но, конечно, работает не так, как ожидалось. Другие подходы вокруг Function<> у меня тоже не сработали.

public class User {
    public Map<Class, Base> map;

    public User(...) {
        ... creates instances of ADerived and BDerived, puts them into map
    }

    ...

    public <R> String getValue(Function<R, String> func) {
        return func.apply((R) map.get((R) Base.class));
    }
}

Это явно не работает по нескольким причинам, но я не могу найти способ использовать описанное выше.

Реально ли вообще получить описанное использование? Есть ли что-то, что я упускаю?

Хотел бы услышать идеи или получить некоторую помощь.


person AndiAn    schedule 21.06.2021    source источник
comment
Когда ключ всегда один и тот же (Base.class), как вы можете предположить, что он сопоставляется с соответствующим вводом функции, которая требует произвольного R?   -  person Holger    schedule 22.06.2021


Ответы (1)


Это невозможно, как вы сейчас делаете. Этот ответ хорошо объясняет это.

Однако вы можете сделать что-то вроде этого:

class User {
    // ...

    public <C extends Base> String getValue(Class<C> cls, Function<C, String> func) {
        return func.apply(cls.cast(map.get(cls)));
    }
}

class Main {
    public static void main(String[] args) {
        User user = new User();
        String value;
        
        value = user.getValue(ADerived.class, ADerived::getPropertyFromA);
        System.out.println(value); // Outputs Test A
        
        value = user.getValue(BDerived.class, BDerived::getPropertyFromB);
        System.out.println(value); // Outputs Test B 
    }
}

Или, если вы хотите избежать повторения имени класса, вы можете использовать имя метода в виде строки. ВНИМАНИЕ: при таком подходе будет потеряна проверка во время компиляции и будет использоваться отражение, но я привожу его здесь только для полноты картины.

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

// ...

class User {
    // ...

    public <C extends Base> String getValue(Class<C> cls, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        final Method method = cls.getMethod(methodName);
        return (String) method.invoke(map.get(cls));
    }
}

class Main {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        User user = new User();
        String value;
        
        value = user.getValue(ADerived.class, "getPropertyFromA");
        System.out.println(value); // Outputs Test A
        
        value = user.getValue(BDerived.class, "getPropertyFromB");
        System.out.println(value); // Outputs Test B 
    }
}

Вы должны справиться

  • NoSuchMethodException когда имя метода не существует в данном классе;

  • IllegalAccessException когда метод, к которому пытаются получить доступ, является защищенным или закрытым;

  • InvocationTargetException когда метод бросает вызов при доступе.

person enzo    schedule 21.06.2021