Using Scala from Java: passing functions as parameters
Consider the following Scala code:
package scala_java
object MyScala {
def setFunc(func: Int => String) {
func(10)
}
}
Now in Java, I would have liked to use MyScala
as:
package scala_java;
public class MyJava {
public static void main(String [] args) {
MyScala.setFunc(myFunc); // This line gives an error
}
public static String myFunc(int someInt) {
return String.valueOf(someInt);
}
}
However, the above does not work (as expected since Java does not allow functional programming). What is the easiest workaround to pass a function in Java? I would like a generic solution that works with functions having arbitrary number of parameters.
EDIT: Does Java 8 have any better syntax than the classic solutions discussed below?
Solution 1:
In the scala.runtime
package, there are abstract classes named AbstractFunction1
and so on for other arities. To use them from Java you only need to override apply
, like this:
Function1<Integer, String> f = new AbstractFunction1<Integer, String>() {
public String apply(Integer someInt) {
return myFunc(someInt);
}
};
If you're on Java 8 and want to use Java 8 lambda syntax for this, check out https://github.com/scala/scala-java8-compat.
Solution 2:
You have to manually instantiate a Function1
in Java. Something like:
final Function1<Integer, String> f = new Function1<Integer, String>() {
public int $tag() {
return Function1$class.$tag(this);
}
public <A> Function1<A, String> compose(Function1<A, Integer> f) {
return Function1$class.compose(this, f);
}
public String apply(Integer someInt) {
return myFunc(someInt);
}
};
MyScala.setFunc(f);
This is taken from Daniel Spiewak’s “Interop Between Java and Scala” article.
Solution 3:
The easiest way for me is to defined a java interface like:
public interface JFunction<A,B> {
public B compute( A a );
}
Then modify your scala code, overloading setFunc
to accept also JFunction
objects such as:
object MyScala {
// API for scala
def setFunc(func: Int => String) {
func(10)
}
// API for java
def setFunc(jFunc: JFunction[Int,String]) {
setFunc( (i:Int) => jFunc.compute(i) )
}
}
You will naturally use the first definition from scala, but still be able to use the second one from java:
public class MyJava {
public static void main(String [] args) {
MyScala.setFunc(myFunc); // This line gives an error
}
public static final JFunction<Integer,String> myFunc =
new JFunction<Integer,String>() {
public String compute( Integer a ) {
return String.valueOf(a);
}
};
}