2011-07-01 23 views
41

考虑下面的Scala代码:从Java使用Scala的:通过功能参数

package scala_java 
object MyScala { 
    def setFunc(func: Int => String) { 
    func(10) 
    } 
} 

现在在Java中,我也喜欢使用MyScala为:

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

然而,上述不不工作(如预期的那样,因为Java不允许函数式编程)。在Java中传递函数最简单的解决方法是什么?我想要一个通用的解决方案,可以与任意数量的参数一起使用。

编辑:Java 8比以下讨论的经典解决方案有更好的语法吗?

回答

25

您必须手动实例化Java中的Function1。喜欢的东西:

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

这是从Daniel Spiewak’s “Interop Between Java and Scala” article拍摄。

+4

请务必查看文章和Daniel的FunUtils类,这有助于删除'$ tag'和'compose'所需的一些样板。 –

+0

这是非常丰富的。我曾看过这篇文章,但没有阅读评论。 – Jus12

7

对我来说,最简单的方法是定义一个的java接口,如:

public interface JFunction<A,B> { 
    public B compute(A a); 
} 

然后修改您的Scala代码,超载setFunc接受也JFunction对象,如:

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

你自然会使用scala的第一个定义,但仍然可以使用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); 
     } 
    }; 

} 
+0

@ Jus12。谢谢,这是固定的。 – paradigmatic

+0

实际上,Scala的函数*已经*在幕后实现为SAM类型。因此,你可以直接实现'FunctionN'(按照Jean-Philippe的回答)并且不需要引入你自己的'JFunction'类型。 –

+0

我不同意,虽然你可以直接从java实例化一个scala函数,但最终的代码相当难看。如果你只是想要访问一些scala代码,那就没关系。但是如果你要提供一个java API,你将通过添加一些为java API设计的更高层接口和scala方法来获得更好的代码。例如,它将有助于解决缺乏隐式解决方案的问题。 – paradigmatic

58

scala.runtime包中,有些抽象类名为AbstractFunction1等等其他arities。如果你对Java的8要使用Java 8 lambda语法对于这一点,看看https://github.com/scala/scala-java8-compat

Function1<Integer, String> f = new AbstractFunction1<Integer, String>() { 
    public String apply(Integer someInt) { 
     return myFunc(someInt); 
    } 
}; 

:从Java中使用它们你只需要重写apply,像这样。

+2

谢谢!你的答案是最有帮助的!试图实现Function1,Java告诉我我必须实现数十种方法--Function1对几种基本类型进行了专门化。 –

+1

This * is *您正在寻找的答案:)另请参阅http://twitter.github.io/scala_school/java.html – alexr

+0

请注意,如果您正在编译Android或iOS,则无法使用使用scala-java8-compat是因为retrolambda只能修改你的源文件。源代码也没有lambda bridge类(JFunction0等),因为它们是从sbt直接代码生成的.class文件。可能有一种方法可以在您的项目中加入.class文件,retrolambda可以修改它们,但我没有投入努力来解决这个问题。 – voxoid

0

这是我的一个解决方案的尝试,一个小图书馆:https://github.com/eirslett/sc8

你包裹在F(...)您的Java 8拉姆达,然后它转换成一个Scala的功能。