Как я могу сопоставить String с функцией в Java?

В настоящее время у меня есть куча классов Java, которые реализуют интерфейс Processor, то есть все они имеют метод processRequest(String key). Идея состоит в том, что каждый класс имеет несколько (например, < 10) членов Strings, и каждая из этих карт относится к методу этого класса с помощью метода processRequest, например:

class FooProcessor implements Processor
{
    String key1 = "abc";
    String key2 = "def";
    String key3 = "ghi";
    // and so on...

    String processRequest(String key)
    {
        String toReturn = null;
        if (key1.equals(key)) toReturn = method1();
        else if (key2.equals(key)) toReturn = method2();
        else if (key3.equals(key)) toReturn = method3();
        // and so on...

        return toReturn;
    }

    String method1() { // do stuff }
    String method2() { // do other stuff }
    String method3() { // do other other stuff }
    // and so on...
}

Вы получаете идею.

Это работало отлично для меня, но теперь мне нужно доступное время исполнения от ключа к функции; не каждая функция фактически возвращает String (некоторый возврат void), и мне нужно динамически обращаться к типу return (используя отражение) каждой функции в каждом классе, для которой есть ключ. У меня уже есть менеджер, который знает обо всех ключах, но не в отображении от ключа к функции.

Мой первый инстинкт заключался в замене этого сопоставления с помощью операторов if-else с помощью Map<String, Function>, как я мог бы сделать в Javascript. Но Java не поддерживает первоклассные функции, поэтому мне там не повезло. Возможно, я мог бы выкопать стороннюю библиотеку, которая позволяет мне работать с первоклассными функциями, но я еще не видел их, и я сомневаюсь, что мне нужна вся новая библиотека.

Я также думал о том, чтобы поместить эти ключи String в массив и использовать отражение для вызова методов по имени, но я вижу два нижних уровня этого метода:

  • Мои ключи должны быть названы так же, как и метод, или должны быть названы определенным, последовательным способом, чтобы было легко сопоставить их с именем метода.
  • Это кажется ПУТЬ медленнее, чем у операторов if-else, которые у меня есть сейчас. Эффективность - это что-то вроде проблемы, потому что эти методы будут часто вызываться довольно часто, и я хочу минимизировать ненужные накладные расходы.

TL; DR: Я ищу чистый, минимальный накладной способ сопоставления String с каким-либо объектом Function, который я могу вызвать и вызвать (что-то вроде) getReturnType() on. Я не особо возражаю против использования сторонней библиотеки, если она действительно соответствует моим потребностям. Я также не возражаю против использования рефлексии, хотя я бы предпочел не использовать рефлексию каждый раз, когда я выполняю поиск метода - возможно, используя некоторую стратегию кэширования, которая объединяет Map с отражением.

Мысли о хорошем способе получить то, что я хочу? Ура!

Ответы

Ответ 1

Не могли бы вы сделать String в Method? Затем вы можете кэшировать методы, необходимые для выполнения.

Ответ 2

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

public interface ComputeString
{
    public String invoke();
}

Затем вы можете создать объект Map<String,ComputeString>, как вы хотите, в первую очередь. Использование карты будет намного быстрее, чем отражение, а также даст больше безопасности типов, поэтому я бы посоветовал выше.

Ответ 3

Пока у вас нет функций первого класса, существуют анонимные классы, которые могут быть основаны на интерфейсе:

interface ProcessingMethod {
   String method();
}

Map<String, ProcessingMethod> methodMap = new HashMap<String, ProcessingMethod>();
methodMap.put("abc", new ProcessingMethod() {
   String method() { return "xyz" }
});
methodMap.put("def", new ProcessingMethod() {
   String method() { return "uvw" }
});

methodMap.get("abc").method();

Или вы можете использовать Scala: -)

Ответ 4

Как вы заметили, вы можете делать то, что хотите, используя Reflection API, но вы потеряете некоторые преимущества компилятора Java, в дополнение к проблемам, которые вы уже придумали. Обертывание ваших строк в объекте и использование Шаблон посетителя разрешит вашу проблему? Каждый StringWrapper принимает только Visitor, который имеет правильный метод или что-то в этом роде.

Ответ 5

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

class FooProcessor implements Processor{

    Map<String, FooMethod> myMap;

    String processRequest(String key){
        FooMethod aMethod = myMap.getValue(key);
        return aMethod.method();
    }        
}

Ответ 6

Этот пример использует enum названных функций и абстрактный FunctionAdapter для вызова функций с переменным числом однородных параметров без отражение. Функция lookup() просто использует Enum.valueOf, но Map может стоить большого количества функций.

Ответ 7

Как насчет Method класс из API отражения? Вы можете найти методы класса на основе имени, параметров или типа возврата. Затем вы просто вызываете Method.invoke(это, параметры).

Это почти то же самое, что и карта с JavaScript, о которой вы говорите.