Using Scala from Java: passing functions as parame

2019-01-13 17:50发布

问题:

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?

回答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.



回答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.



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

}


回答4:

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.