2015-07-15 2 views
2

Можно ли создать java.util.function.Function из String с использованием отражения? Кажется, я не могу понять, как это сделать, поскольку нет конструкторов для Function.Построить функцию во время выполнения

Так что у меня есть этот метод:

public Integer hello() { 
    return 5; 
} 

Я хочу получить Function ссылку для метода hello(). Мне нужно передать имя метода, в данном случае, «привет» и получить Function.

я могу получить Function таким способом также, Test::hello, но я хочу сделать что-то подобное, Test::"hello"


@immibis упомянутые попробовать это:

private Function<Test, Integer> getFunction(String s){ 
    return (Test o) -> s; 
}     ^

Но, к сожалению, делает не работает, называя это так, getFunction("hello"). IntelliJ IDEA дает мне сообщение об ошибке:

Bad return type in lambda expression: String cannot be converted to Integer 

Это не дубликат this, я не хочу Method ответ, но вместо Function ответ.

+0

'Function' - это интерфейс ... – immibis

+0

ОК, я хочу создать ссылку на метод, который может быть сохранен в переменной' Function'. –

+0

Почему вы хотите это сделать? –

ответ

3

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

Тем не менее, неясно, из вашего вопроса, что именно динамичность вы ожидаете:

  1. Фактическое кодирование Java
  2. оценки простых выражений, как 1+2*(4-8)
  3. или какой-либо другой сценарий, как конструкт вы хотите, чтобы разобрать и оценить

Для реального Java-кодирования я бы предложил реализовать некоторую абстракцию с использованием пары API/SPI. SPI - это интерфейс поставщика услуг или абстракция, которая позволяет другим предоставлять готовые и скомпилированные классы в качестве расширения. Я считаю, OSGI предоставляет стандартный способ сделать это.

Для оценки выражений существует множество сторонних библиотек. Я разработал один, но не упомянул, так как есть много других. На этой доске нет цели выдвинуть один инструмент над заказом. Вы также можете рассмотреть Nashorn, который является механизмом JavaScript.

Чтобы разрешить сценарии, я бы предложил придерживаться javascript и использовать Nashorn. Java позволяет создавать плагины и фактически позволяет добавлять дополнительные скриптовые двигатели как часть JSR-223.

[UPDATE]

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

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

import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 
import java.util.function.Function; 

public class Play { 
    public int hello() { 
    return 5; 
    } 

    static public int byebye() { 
    return -1; 
    } 

    public static class ExtendedPlay extends Play { 
    @Override 
    public int hello() { 
     return 10; 
    } 
    } 

    private static <T> Function<T,Integer> getFunction(Class<T> clazz,String method) throws NoSuchMethodException { 
    Method m = clazz.getDeclaredMethod(method); 
    return (o)->{ 
     try { 
     return ((Integer)m.invoke(o)); 
     } catch (IllegalAccessException | InvocationTargetException ex) { 
     // Just hope and pray this will be all ok! 
     } 
     return 0; 
    }; 
    } 

    private static <T> Function<Class<T>,Integer> getStaticFunction(Class<T> clazz,String method) throws NoSuchMethodException { 
    Method m = clazz.getDeclaredMethod(method); 
    return (o)->{ 
     try { 
     return ((Integer)m.invoke(o)); 
     } catch (IllegalAccessException | InvocationTargetException ex) { 
     // Just hope and pray this will be all ok! 
     } 
     return 0; 
    }; 
    } 

    private static Function<Object,Integer> getFunction(String method) { 
    return (o)->{ 
     try { 
     Method m; 
     if (o instanceof Class) // For static methods 
      m = ((Class)o).getDeclaredMethod(method); 
     else // For instance methods 
      m = o.getClass().getDeclaredMethod(method); 
     return ((Integer)m.invoke(o)); 
     } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) { 
     // Just hope and pray this will be all ok! 
     } 
     return 0; 
    }; 
    } 

    public static void main(String args[]) throws NoSuchMethodException { 
    // Little quicker because class type and Method instance can be resolved before multiple executions. 
    // Method is cached and has better compile-time type checking, but requires extra paramter. 
    Function<Play,Integer> f1 = getFunction(Play.class,"hello"); 
    Function<Class<Play>,Integer> f2 = getStaticFunction(Play.class,"byebye"); 

    // Little slower, because method instance has to be resolved for each subsequent call 
    // of the dereferenced Function Object. Slower but use is simpler: one less parameter, and works for 
    // both static and instance methods. 
    Function<Object,Integer> f3 = getFunction("hello"); 

    System.out.println("Value1 is: "+f1.apply(new ExtendedPlay())); 
    System.out.println("Value2 is: "+f2.apply(Play.class)); 
    System.out.println("Value3 is: "+f3.apply(new Play())); 
    } 
} 

Обратите внимание, что я принял решение таким образом, чтобы оно работало как с статическими методами, так и с экземплярами.

+0

«Число возможностей» изменяется по мере изменения моего кода, я не хочу добавлять больше случаев для каждого метода. –

+0

ОК, почти работает, но идея «Функция» в моем случае состоит в том, чтобы иметь возможность изменять объект и по-прежнему применять «Функцию» с помощью функции function.apply (theObject). В вашей 'getFunction (Object, String)', вы сохраняете объект при создании функции, и это фактически окончательно. Использование очень похоже на создание «метода» и вызов его на разные объекты в разное время. –

+0

Я использую это для управления настройками в графическом интерфейсе. Идея состоит в том, что объект 'Settings' также может быть установлен на' SettingsPane'. Все настройки, находящиеся внутри 'SettingsPane', также привязаны к результату' Function'. Таким образом, каждое 'Setting' имеет свойство, которое содержит' Function'. Когда изменяется параметр «Настройки», эта функция должна быть снова применена к новому объекту «Настройки». Обратите внимание на разницу между 'Setting' и' Settings'. –

2

@ Ответ от JoD. Правильный. Вот я беру еще один подход к решению проблемы, без использования отражения:

public class Test { 

    private final int number; 

    public Test(int number) { 
     this.number = number; 
    } 

    public int increment() { 
     return this.number + 1; 
    } 

    public int decrement() { 
     return this.number - 1; 
    } 

    public static void main(String[] args) { 

     // Define references to methods  
     Function<Test, Integer> incr = Test::increment; 
     Function<Test, Integer> decr = Test::decrement; 

     // Store method references in a map  
     Map<String, Function<Test, Integer>> map = new HashMap<>(); 
     map.put("incr", incr); 
     map.put("decr", decr); 

     // Define invocation: based on a string, select method reference to use 
     Function<String, Function<Test, Integer>> invocation = k -> map.get(k); 

     // Now the test 
     Test test1 = new Test(10); 

     int incrOnTest1 = invocation.apply("incr").apply(test1); 
     int decrOnTest1 = invocation.apply("decr").apply(test1); 

     System.out.println(incrOnTest1); // 11 
     System.out.println(decrOnTest1); // 9 

     Test test2 = new Test(50); 

     int incrOnTest2 = invocation.apply("incr").apply(test2); 
     int decrOnTest2 = invocation.apply("decr").apply(test2); 

     System.out.println(incrOnTest2); // 51 
     System.out.println(decrOnTest2); // 49 
    } 
} 

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

+0

Это действительно хорошее решение, но вы указали: «Число изменений изменяется по мере изменения моего кода, я не хочу добавлять больше случаев для каждого метода». Так как же теперь все иначе? – YoYo

+1

@JoD. Это не я. Это был ОП. В любом случае, вы правы. При моем подходе, если к классу добавлен новый метод, вы должны добавить его к карте. Возможно, лучшим решением было бы использовать отражение для сканирования класса и добавления найденных методов на карту при загрузке приложения, но это кажется излишним и чрезвычайно сложным, поскольку вам придется динамически создавать функции, содержащие ссылки на методы, основанные на по отраженным методам. –

+1

Да, вы как-то ошибочно приняли вас за OP ... - Я действительно прошел через решение, подобное вашему, которое было отредактировано после целого дня взаимодействия с OP. Карта - это то, о чем я думал. Не думайте, что это очень сложно, но не уверен, насколько это поможет. – YoYo

Смежные вопросы