1

I have a method that selects between the arguments of an array and returns a specific one. For instance, here is that method:

private <T> T selectOnType(T[] selection, T defaultOp){
    switch(this.type){
        case Resources.TEXT:
            return selection[Resources.TEXT];
        case Resources.LISTEN:
            return selection[Resources.LISTEN];
        default:
            return defaultOp;
    }
}

How can I construct an array full of method references (i.e. function pointers) in order to be able to pass that array into this method above?

I tried doing such things as:

java.util.function.Function<Void, Void>[] array = {ClassA::method1, ClassA::method2};

(where method1 and method1 take no arguments and return void)

But that throws a compiler error saying:

incompatible types: invalid method reference but expected no arguments.
    found: java.lang.Void
    reason: actual and formal argument lists differ in length

I have been playing around with lambdas such as:

() -> ClassA.method1()

But I haven't been able to get it to work. Does anyone know what I am doing wrong and know a solution to this problem?

EDIT: I have seen this on Stack Overflow, but this is for C# and I haven't figured out how to mimic it in Java.

Example:
Let's say I have a Word class:

public class Word{
    private final String text;
    private int listenCorrect = 0, textCorrect = 0;
    public Word(final String test){
        this.text = text;
    }
    public void incListenCorrect(){
        listenCorrect++;
    }
    public void incTextCorrect(){
        textCorrect--;
    }
}

And finally I have a Main class. Inside the action method (in the Main class) I want to have an array with these two methods in it in order to select between them if the type (shown below) is either listen or text:

public class Main{
    int type = 0;
    public void action(){
        Word word = new Word("Hello");
        // 'Functions' is used to represent something I tried above (just for demonstration)
        Function[] array = {word::incListenCorrect, word::incTextCorrect};
        Function picked = selectOnType(array, word::incTextCorrect);
        picked.call();
    }
    /*
     *  Resources is another class that contains the following values:
     *            public static final int TEXT       = 0;
     *            public static final int LISTEN     = 1;
     */
    private <T> T selectOnType(T[] selection, T defaultOp){
        switch(this.type){
            case Resources.TEXT:
                return selection[Resources.TEXT];
            case Resources.LISTEN:
                return selection[Resources.LISTEN];
            default:
                return defaultOp;
        }
    }
}
4
  • 1
    Are method1 and method2 instance methods or static methods? Commented Oct 9, 2016 at 2:10
  • your methods are not equivalent to Function<Void, Void>, but to Runnable Commented Oct 9, 2016 at 2:13
  • @ajb Take a look at the example I included; they are instance methods. Commented Oct 9, 2016 at 2:15
  • 1
    Use list of Runnable like List<Runnable> array = Arrays.asList(word::incListenCorrect, word::incTextCorrect);. Commented Oct 9, 2016 at 2:22

1 Answer 1

3

A Function is a method that takes one argument and returns a result. You're using methods that take no arguments and do not return results. You can't use Function for this (using Void isn't a way to get around this), but the java.util.function package contains a number of classes for different common combinations (methods that take no arguments but return a result, methods that take one or two arguments and don't return a result, methods that take primitive arguments or return primitive results that won't work in a Function because the types aren't class types, etc.).

There isn't a class in java.util.function for a functional interface with no arguments and no result, but Runnable can be used for that.

You need to make sure you use the correct interface.

Note: I was assuming method1 and method2 are static methods, so that they don't take any arguments, even a hidden "instance" argument that instance methods take. If they're instance methods, then things have to be done differently.

Now that you've clarified that they're instance methods, things are different--but it depends on how you get the method. If you say

Word::incListenCorrect

since you're using the class name, you need to provide the instance as an argument. Therefore, Word::incListenCorrect returns a functional interface for a method that takes one argument, such as Consumer<Word>, and you have to pass the Word as the argument when you call the method with .accept(). But:

word::incListenCorrect

is very different. Now, the word instance becomes "baked into" the method reference, so it doesn't need to be passed as an argument. In this case, therefore, you'll still need the interface that takes no arguments and does not return a value, which is Runnable. When you say

Runnable r = word::incListenCorrect;    
r.run();

where r is a Runnable, it will automatically use word as the instance for the instance method, since word became part of r when you assigned the method reference to it.

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

7 Comments

I'll take a look at this. Also, take a look at the example I added too for further clarification of what I am doing (i.e. they aren't static methods)
You, my friend, are fantastic! :) Quick question for you, if I had an instance method (like the example above) that took "x" number of arguments and return a result, would I use lambdas for that? I guess I couldn't wrap it into a Runnable object anymore because the run() method takes no arguments and is a void function, so what would I wrap it into then?
The problem here is that you need the correct type, whether it's Runnable, Consumer<T>, Function<T,U>, or whatever. If there are 1 or 2 arguments, you can use one of the interfaces in java.util.function (unless some of the arguments are primitive types, and then you may or may not be able to). If there are >=3 arguments, you'll need to define your own interface type, and then you can use a method reference. A lambda won't help you get around the need to define your own type.
So a function like public int echo(int x) { return x; } would not be able to be used by java.util.function because of the primitive types (as stated by you above), but could I create an interface, like how you said, to be able to handle something like this? Or am I force to wrap primitives in objects?
Actually, java.util.function does have IntUnaryOperator that would be able to handle exactly that kind of function. You just have to look at the classes listed there and figure out whether it's supported. If it weren't supported, then yes, you could create your own interface.
|

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.