Как я могу сопоставить строку с функцией в Java? - PullRequest
11 голосов
/ 30 апреля 2010

В настоящее время у меня есть несколько классов Java, которые реализуют интерфейс Processor, то есть все они имеют метод processRequest(String key). Идея состоит в том, что у каждого класса есть несколько (скажем, <10) членов <code>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 (некоторая возвращаемая пустота), и мне нужно динамически обращаться к типу возврата (используя отражение) каждой функции в каждом классе, для которого есть ключ. У меня уже есть менеджер, который знает обо всех ключах, но не отображение ключа на функцию.

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

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

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

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

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

Ответы [ 7 ]

7 голосов
/ 30 апреля 2010

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

public interface ComputeString
{
    public String invoke();
}

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

4 голосов
/ 30 апреля 2010

Хотя у вас не может быть функций первого класса, есть анонимные классы, которые могут основываться на интерфейсе:

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: -)

1 голос
/ 25 марта 2011

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

1 голос
/ 30 апреля 2010

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

0 голосов
/ 01 мая 2010

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

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

0 голосов
/ 30 апреля 2010

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

class FooProcessor implements Processor{

    Map<String, FooMethod> myMap;

    String processRequest(String key){
        FooMethod aMethod = myMap.getValue(key);
        return aMethod.method();
    }        
}
0 голосов
/ 30 апреля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...