Using Scala from Java: passing functions as parameters

22,208

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

}
Share:
22,208
Jus12
Author by

Jus12

Updated on July 09, 2022

Comments

  • Jus12
    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
    Jean-Philippe Pellet almost 13 years
    Be sure to check out the article and Daniel's FunUtils class in the comments, which helps remove some of the needed boilerplate for $tag and compose.
  • Jus12
    Jus12 almost 13 years
    That is very informative. I had seen the article before but not read the comments.
  • paradigmatic
    paradigmatic almost 13 years
    @Jus12. Thanks, that's fixed.
  • Kevin Wright
    Kevin Wright almost 13 years
    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.
  • paradigmatic
    paradigmatic almost 13 years
    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.
  • Jus12
    Jus12 almost 13 years
    This is fine as long as you have the Scala source.
  • Kevin Wright
    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
    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
    jcsahnwaldt Reinstate Monica almost 12 years
    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.
  • alexr
    alexr about 11 years
    This is the answer you are looking for :) Also check out twitter.github.io/scala_school/java.html
  • voxoid
    voxoid about 8 years
    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
    voxoid about 8 years
    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<BoxedUnit, BoxedUnit>() { public BoxedUnit apply(BoxedUnit unit) { /* . . . */ return BoxedUnit.UNIT; } }
  • Gautham
    Gautham over 3 years
    @Jean-PhilippePellet it seems like the Daniel Spiewak's link is broken.
  • Jean-Philippe Pellet
    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…