在运行时构造一个Function

时间:2015-07-15 23:46:37

标签: java function reflection

是否可以使用反射从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响应。

2 个答案:

答案 0 :(得分:4)

直观地说,我们已经在我们通常编写的函数(方法)中加入了很多动力:我们使用各种条件分支和循环。如果你可以限制可以做什么,你可以使用这些简单的结构来构建你的函数。

然而,从你的问题中不清楚你期望的确切的动力:

  1. 实际java编码
  2. 评估1+2*(4-8)
  3. 等简单表达式
  4. 或其他一些类似你要解析和评估的构建
  5. 的脚本

    对于实际的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()));
      }
    }
    

    请注意,我以这样的方式创建了解决方案,它可以同时用于静态方法和实例方法。

答案 1 :(得分: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
    }
}

我们的想法是将方法的引用声明为函数,并将它们存储在地图中,并用一些字符串键入。然后,定义一个特殊的调用函数,它接收一个字符串并查询映射以返回相应的方法引用。最后,返回的函数将应用所需的对象intance。

相关问题