49

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?

Jus12
  • 17,824
  • 28
  • 99
  • 157

4 Answers4

75

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.

Seth Tisue
  • 29,985
  • 11
  • 82
  • 149
  • 2
    Thanks! 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. – jcsahnwaldt Reinstate Monica May 06 '12 at 23:04
  • 1
    This *is* the answer you are looking for :) Also check out http://twitter.github.io/scala_school/java.html – alexr Apr 26 '13 at 22:06
  • Note 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 Mar 29 '16 at 12:02
  • 1
    If 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() { public BoxedUnit apply(BoxedUnit unit) { /* . . . */ return BoxedUnit.UNIT; } }` – voxoid Mar 29 '16 at 12:07
27

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.

Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234
7

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

}
paradigmatic
  • 40,153
  • 18
  • 88
  • 147
  • @Jus12. Thanks, that's fixed. – paradigmatic Jul 01 '11 at 08:41
  • Actually, 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 own `JFunction` type. – Kevin Wright Jul 01 '11 at 09:07
  • I 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. – paradigmatic Jul 01 '11 at 09:30
  • This is fine as long as you have the Scala source. – Jus12 Jul 01 '11 at 16:13
  • @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. – Kevin Wright Jul 01 '11 at 19:34
  • @Kevin Wright. Of course, or even from FunctionalJava. That was just an example of what could be done. – paradigmatic Jul 01 '11 at 20:16
0

Here's my attempt at a solution, a little library: https://github.com/eirslett/sc8

You wrap your Java 8 lambda in F(...) and then it's converted to a Scala function.