2

Is it possible to create a java.util.function.Function from a String using reflection? I can't seem to figure out a way to do that as there are no constructors for Function.

So say I have this method:

public Integer hello() {
    return 5;
}

I want to obtain a Function reference for the hello() method. I need to pass the name of the method, in this case, "hello" and get the Function.

I can get the Function in this way also, Test::hello, but I want to do something like, Test::"hello"


@immibis mentioned to try this:

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

But this sadly does not work when calling it like this, getFunction("hello"). IntelliJ IDEA gives me this error message:

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

This is not a duplicate of this, I do not want a Method response, but instead a Function response.

Community
  • 1
  • 1
Chris Smith
  • 2,928
  • 4
  • 27
  • 59
  • `Function` is an interface... – user253751 Jul 15 '15 at 23:50
  • OK, I want to create a reference to a method that can be stored in a `Function` variable. – Chris Smith Jul 15 '15 at 23:51
  • Why do you want to do this? – Vince Jul 16 '15 at 00:10
  • Here's **a** way to construct a Function from a string, without even using reflection: `public Function getFunction(String s) {return (Object o) -> s;}`. If that's not what you had in mind, then you need to be more specific. – user253751 Jul 16 '15 at 00:15
  • I some FXML code that uses a custom control. The custom control has a `Function` property and I need to set that through the FXML. I decided I could add another property that is a string and set that with the FXML, then convert the string to a `Function`. – Chris Smith Jul 16 '15 at 00:17
  • @immibis I need the return to be `Function>`, it appears that after the `->` must be the same type as the input? I need to get a function, say it's `setText()`, from the string `setText`. – Chris Smith Jul 16 '15 at 00:27
  • @ChristopherSmith Does `setText` take a `Settings` and return a `Property>`? – user253751 Jul 16 '15 at 01:26
  • @ChristopherSmith Also my suggestion was not a serious one, I just meant to show that your question was too vague - `(Object o) -> s` is a function that ignores its argument, and always returns `s`. – user253751 Jul 16 '15 at 01:26
  • @immibis `setText` was just an example, instead, let's say, the method is `visibleProperty()` and returns a `BooleanProperty`, I need a `Function` from the string `visibleProperty` – Chris Smith Jul 16 '15 at 01:42
  • 1
    But now after all that work, I also realize that I probably gave more help than I should have. To be respectful to the the community, it is kind of expected for you to come up with part of the solution ... we should have seen an attempt to provide actual code using reflection. – YoYo Jul 16 '15 at 02:11
  • Do your `hello()` methods all have the same signature? – fps Jul 16 '15 at 04:42
  • @FedericoPeraltaSchaffner What do you mean by signature? – Chris Smith Jul 17 '15 at 02:36
  • @JoD. I didn't think this was possible what you did. I am not that familiar with Lambdas and Functions, I just use them. I was thinking I needed to instantiate a `Function` with reflection, but couldn't find any way too. Your answer expanded my knowledge of Lambdas greatly! – Chris Smith Jul 17 '15 at 02:38
  • @ChristopherSmith I mean if they return the same type and accept the same number of parameters of the same type in the same order. – fps Jul 17 '15 at 02:44
  • @FedericoPeraltaSchaffner Yes, the methods are exactly the same in exactly the same class. The only thing changing is the instance. – Chris Smith Jul 17 '15 at 02:52
  • @ChristopherSmith But only one method or more than one? – fps Jul 17 '15 at 03:09
  • I mean, you have several instances of the same class, but more than one method, so you want to specify the method by name and apply the function that corresponds to that method, to the instances you want. Is this correct? – fps Jul 17 '15 at 03:13
  • @FedericoPeraltaSchaffner Yes, that is correct. I want to get the `Function` object for whatever method name I give it. Then apply that `Function` too whatever object I give it, (as long a it is the correct object). – Chris Smith Jul 17 '15 at 03:45
  • @ChristopherSmith Please see my answer. – fps Jul 17 '15 at 04:14

2 Answers2

4

Intuitively we already put a lot of dynamism into functions (methods) we commonly write: we use all sorts of conditional branching and looping. If you can some how constrain what can be done, you can build your function using those simple constructs.

However, it is unclear from your question exactly what dynamism you expect:

  1. Actual java coding
  2. evaluating simple expressions like 1+2*(4-8)
  3. or some other script like construct you want to parse and evaluate

For actual Java coding, I would suggest to implement some kind of abstraction using an API/SPI pair. An SPI is a Service Provider Interface, or an abstraction that allows others to provide ready made and compiled classes as an extension. I believe OSGI provides a standard way of doing this.

To evaluate expressions there are many 3rd party libraries available. I developed one, but wont mention, as there are many others available. This board does not have the purpose to put forward one tool over the order. You can also consider Nashorn, which is a JavaScript engine.

To actually allow scripting, I would suggest to stick with javascript and use Nashorn. Java allows for plugins and actually enables you to add-in additional scripting engines as part of JSR-223.

[UPDATE]

Based on your clarifications and your example, yes we will need to use some type of reflection.

In your case, you want to lazily decide to which class or instance you are going to apply the method. This constrains me to provide the solution as below, however I took it one step further by optimizing the implementation for one case: where the class of instances the functional objects will be applied or can be predetermined.

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()));
  }
}

Note that I made the solution in such a way that it would work on both static and instance methods.

YoYo
  • 9,157
  • 8
  • 57
  • 74
  • "The number of possibilities" changes as my code changes, I don't want to have to be adding more cases for every method. – Chris Smith Jul 16 '15 at 01:16
  • OK, almost works, but the idea of the `Function` in my case is to be able to change the object, and still apply the `Function` with `function.apply(theObject)`. In your `getFunction(Object, String)`, you save the object when you create the function and it is effectively final. The use is very similar to creating a `Method` and invoking it on different objects at different times. – Chris Smith Jul 16 '15 at 01:52
  • I am using this for a settings control in a GUI. The idea is that a `Settings` object can be set too the `SettingsPane`. All the settings controls inside the `SettingsPane` are binded too the result of the `Function`. So each `Setting` has a property that holds a `Function`. When the `Settings` change, this function has to be re-applied too the new `Settings` object. Note the difference between `Setting` and `Settings`. – Chris Smith Jul 16 '15 at 02:34
  • Now you might ask, "Why not use `Method` instead of `Function`?" Well in other parts of my program, I am using `EasyBind` to bind nested properties, and this requires the functional stuffs. – Chris Smith Jul 16 '15 at 02:41
  • I am having some issues with proxies applying my functions, I will accept this as the answer as soon as I get it to work correctly. – Chris Smith Jul 16 '15 at 02:45
  • Not sure I understood the last use cases given, but I went ahead and added the discussed optimizations in. Up to you to decide if you can go that route or not. – YoYo Jul 16 '15 at 07:44
  • I am not sure how you made optimizations in your last update? – Chris Smith Jul 17 '15 at 01:21
  • This fixed my problem, I was having other issues with classloaders. I ended up getting rid of the reflection and using normal types. It is much more readable now! Thanks! – Chris Smith Jul 17 '15 at 02:36
2

@JoD.'s answer is correct. Here I'm taking another approach to solve the problem, without using reflection:

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
    }
}

The idea is to declare references to methods as functions, and store them in a map, keyed by some string. Then, a special invocation function is defined, which receives a string and queries the map to return the corresponding method reference. Finally, the returned function is applied with the desired object intance.

fps
  • 33,623
  • 8
  • 55
  • 110
  • Which is really a good solution, but you stated "The number of possibilities changes as my code changes, I don't want to have to be adding more cases for every method.". So how is that now any different? – YoYo Jul 17 '15 at 06:57
  • 1
    @JoD. It wasnt me. It was the OP. Anyways, you are right. With my approach, if a new method is added to the class, you have to add it to the map. Maybe the best solution would be to use reflection to scan the class and add found methods to the map when the application is loading, but that seems overkill and extremely complex, since you would have to dynamically create the functions that hold the method references, based on the reflected methods. – fps Jul 17 '15 at 10:17
  • 1
    yeah - mistook you actually for the OP somehow ... - I actually went through a solution similar to yours already - which got edited out after a whole day of interaction with the OP. The Map is also something I thought about. Don't think it is highly complex, but not sure how much it will help. – YoYo Jul 17 '15 at 18:59
  • @JoD. The tough part is to dynamically create a lambda targeting the method, without using reflection inside lambda's body. This could be done using [`LambdaMetafactory`](https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html) utility, which is a highly complex piece of software. – fps Jul 17 '15 at 19:02
  • Actually - just thought about another way that merge both worlds: using annotations & CDI. No reflection, but you can still wire things together using configuration (xml), plus you can do it in-code as well. – YoYo Jul 17 '15 at 19:02
  • Well, you could write a sketch of that solution here or somewhere else ;) It would be interesting to know... – fps Jul 17 '15 at 19:06
  • This would work, but it uses strings for keys, which I moved a way from in my original code, to get these method references. If I were to rename a field, it would cause hiccups everywhere. As said above, I don't want to add a line of code for every time I change something. I could use reflection to do it for me, but I think that it isn't really worth it when I could use @JoD.'s answer, yes, it is slower, but this function of the program is rarely used. It's the "restore default settings" button. – Chris Smith Jul 18 '15 at 22:54
  • @ChristopherSmith As I told JoD., what you want can be done using reflection to scan the class and create a lambda dynamically with LambdaMetafactory class. I'll update my answer with that approach when I get the chance. – fps Jul 18 '15 at 23:33
  • There is still the string literals when retrieving the function. I am interested in seeing how `LambdaMetaFactory` works though. – Chris Smith Jul 19 '15 at 00:08