Using Scala from Java: passing functions as parameters
Solution 1
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 2
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 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);
}
};
}
Jus12
Updated on July 09, 2022Comments
-
Jus12 almost 2 years
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?
-
Jean-Philippe Pellet almost 13 yearsBe sure to check out the article and Daniel's
FunUtils
class in the comments, which helps remove some of the needed boilerplate for$tag
andcompose
. -
Jus12 almost 13 yearsThat is very informative. I had seen the article before but not read the comments.
-
paradigmatic almost 13 years@Jus12. Thanks, that's fixed.
-
Kevin Wright almost 13 yearsActually, Scala's functions are already implemented as a SAM type behind the scenes. You can therefore directly implement
FunctionN
(as per Jean-Philippe's answer) and don't need to introduce your ownJFunction
type. -
paradigmatic almost 13 yearsI disagree, while you can directly instantiate a scala function from java, the resulting code is rather ugly. If you just want to access some scala code sometimes, it is ok. But if you to provide a java API, you'll have a nicer code by adding some higher level interface and scala methods designed for the java API. It will help a lot to cope with the lack of implicit resolutions for example.
-
Jus12 almost 13 yearsThis is fine as long as you have the Scala source.
-
Kevin Wright almost 13 years@paradigmatic - Even then, there's a strong argument to be made for using something like the
Function
type from Google guava, instead of re-inventing the wheel. -
paradigmatic almost 13 years@Kevin Wright. Of course, or even from FunctionalJava. That was just an example of what could be done.
-
jcsahnwaldt Reinstate Monica almost 12 yearsThanks! Your answer is the most helpful of all! Trying to implement Function1, Java tells me I have to implement dozens of methods - Function1 has specializations for several primitive types.
-
alexr about 11 yearsThis is the answer you are looking for :) Also check out twitter.github.io/scala_school/java.html
-
voxoid about 8 yearsNote that if you're compiling for Android or iOS, you won't be able to use scala-java8-compat because retrolambda can only modify what you have source files for. Nor does the source have the lambda bridge classes (JFunction0 etc) because they are code-generated from the sbt directly to .class files. Possibly there is a way to include the .class files in your project where retrolambda can modify them, but I haven't invested the effort to figure that out.
-
voxoid about 8 yearsIf your function parameter takes no parameters, you need to type an AbstractFunction1 with BoxedUnit; similarly, if it returns Unit, you need to type the return valies with BoxedUnit and return BoxedUnit.UNIT in your lambda. i.e.
new AbstractFunction1<BoxedUnit, BoxedUnit>() { public BoxedUnit apply(BoxedUnit unit) { /* . . . */ return BoxedUnit.UNIT; } }
-
Gautham over 3 years@Jean-PhilippePellet it seems like the Daniel Spiewak's link is broken.
-
Jean-Philippe Pellet over 3 years@Gautham Check out Seth's answer, it's better than mine. Daniel's link is now about 10 years old…