Dan Hoynoski Dan Hoynoski - 1 month ago 10
Java Question

Array Containing Methods/Functions for Argument Passing [Java 8]

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

ajb ajb
Answer

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.