2011-07-01 52 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的功能。