-1

I am trying to use a list of function references as a lookup table (avoiding the need for a long switch statement). The code worked for a list of static methods, but when I tried to use non-static (i.e. instance) methods in the list, Java gives several errors regarding the types not matching.

Here is a minimal example:

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class MethodReferences {
    // My original list of static references
    private final static List<Function<Integer, Integer>> lookupTable = Arrays.asList(MethodReferences::f1, MethodReferences::f2);

    // This doesn't work
    // private final List<Function<Integer, Integer>> lookupTable = Arrays.asList(MethodReferences::f3, MethodReferences::f4);

    private static int f1(int x) { return x * 2; }

    private static int f2(int x) { return x * 3; }

    private int f3(int x) { return x * 2; }

    private int f4(int x) { return x * 3; }

    public void run() {
        System.out.println(lookupTable.get(1).apply(3));
    }

    public static void main(String[] args) {
        MethodReferences testClass = new MethodReferences();

        testClass.run();
    }
}

The errors I received were all for the line containing the non-static definition:

Type mismatch: cannot convert from List<Object> to List<Function<Integer,Integer>>

and:

The target type of this expression must be a functional interface

I tried using this:: instead of MethodReferences:: before the function names. The code then compiled, but when it runs, nothing happens, probably because this has to be used within non-static functions. I then moved the initialisation of the array (still using this:: to within the class constructor, but it continued to produce no output when run.

I've checked through the documentation and tutorials on method references in Java, but I cannot find an examples of creating references to instance methods within the class it is defined in (and I cannot find any examples of lists of function references either).

I'm aware that in the main method, you can do testClass::f1, but for my specific situation (not the example code) I do not even have a main class (the class is instantiated by another library), so this approach isn't possible. The methods have to be non-static because I need to be able to modify instance variables within them.


Edit: It turns out that using this:: does work for the example code, although I am still unsure as to why it is valid (surely you can only use this within a non-static function?)

5
  • 2
    sry, cannot reproduce: it compiles & runs as expected. (private final List<Function<Integer, Integer>> lookupTable = Arrays.asList(this::f3, this::f4);, prints 9) ... $ ./java.exe -version java version "18.0.2.1" 2022-08-18 Java(TM) SE Runtime Environment (build 18.0.2.1+1-1) Java HotSpot(TM) 64-Bit Server VM (build 18.0.2.1+1-1, mixed mode, sharing) Commented Nov 26, 2022 at 12:17
  • Ok, I tried it on a different machine and I'm not sure why it wasn't working initially. I can confirm that it works with OpenJDK 11 and 17, however I am certain that it didn't work when being instantiated by another class. Using a BiFunction definitely works though. Commented Nov 26, 2022 at 12:32
  • Does this answer your question? Method reference for static and instance methods Commented Nov 26, 2022 at 12:42
  • @DidierL It definitely sheds a little more light on it, but unfortunately it isn't clear that for functions with an input parameter, you need BiFunction instead, and the class must go first. Commented Nov 26, 2022 at 12:51
  • Well you need a BiFunction if you want to to let the caller choose which instance of the containing class to use, otherwise using a simple Function with this:: makes it easier both sides. Commented Nov 26, 2022 at 15:07

2 Answers 2

0

You need to use BiFunction instead of Function. The first argument is the implicit this argument.

public class MethodReferences {
    private final static List<BiFunction<MethodReferences, Integer, Integer>> lookupTable
        = Arrays.asList(MethodReferences::f3, MethodReferences::f4);

    private int f3(int x) { return x * 2; }
    private int f4(int x) { return x * 3; }

    public void run() {
        System.out.println(lookupTable.get(1).apply(this, 3));
    }

    public static void main(String[] args) {
        MethodReferences testClass = new MethodReferences();
        testClass.run();
    }
}

output:

9
Sign up to request clarification or add additional context in comments.

Comments

0

For instance method references which use the ClassName::functionName format, instead of instanceName::functionName, you also need to pass the specific instance of the class to the function when calling .apply().

This means that your method references are actually need to be a BiFunction<MethodReferences, Integer, Integer>, even though there is only one explicit parameter to the function.

When calling the method, you also need to pass this into apply:

import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;

public class MethodReferences {
    // To refer to non-static methods by class name,
    // you must pass in the instance explicitly:
    private final List<BiFunction<MethodReferences, Integer, Integer>> lookupTable = Arrays.asList(MethodReferences::f3, MethodReferences::f4);

    private int f3(int x) {
        return x * 2;
    }

    private int f4(int x) {
        return x * 3;
    }

    public void run() {
        // We need to pass this in, because it isn't implicit
        // for ClassName::functionName references:
        System.out.println(lookupTable.get(1).apply(3));
    }

    public static void main(String[] args) {
        MethodReferences testClass = new MethodReferences();

        testClass.run();
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.