2011-07-01 26 views
41

xem xét mã Scala sau:Sử dụng Scala từ Java: đi qua các chức năng như thông số

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

Bây giờ trong Java, tôi đã có thể thích sử dụng MyScala như:

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

Tuy nhiên, thực hiện trên không hoạt động (như mong đợi vì Java không cho phép lập trình hàm). Cách giải quyết dễ nhất để chuyển một hàm trong Java là gì? Tôi muốn một giải pháp chung mà làm việc với các chức năng có số lượng tham số tùy ý.

CHỈNH SỬA: Java 8 có cú pháp nào tốt hơn các giải pháp cổ điển được thảo luận dưới đây không?

Trả lời

25

Bạn phải tự tạo nhanh Function1 bằng Java. Một cái gì đó như:

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

Điều này được lấy từ Daniel Spiewak’s “Interop Between Java and Scala” article.

+4

Hãy chắc chắn để kiểm tra bài viết và lớp 'FunUtils' của Daniel trong các ý kiến, giúp loại bỏ một số boilerplate cần thiết cho' $ tag' và 'compose'. –

+0

Điều đó rất thông tin. Tôi đã từng xem bài viết trước nhưng không đọc nhận xét. – Jus12

7

Cách đơn giản nhất đối với tôi là để định nghĩa một java giao diện như:

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

Sau đó, sửa đổi mã scala của bạn, quá tải setFunc chấp nhận cũng JFunction các đối tượng như:

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

Bạn sẽ tự nhiên sử dụng định nghĩa đầu tiên từ scala, nhưng vẫn có thể sử dụng định nghĩa thứ hai từ 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. Cảm ơn, đó là cố định. – paradigmatic

+0

Thực ra, các chức năng của Scala đã được * đã thực hiện như một loại SAM đằng sau hậu trường. Do đó, bạn có thể trực tiếp thực hiện 'FunctionN' (theo câu trả lời của Jean-Philippe) và không cần phải giới thiệu kiểu' JFunction' của riêng bạn. –

+0

Tôi không đồng ý, trong khi bạn có thể trực tiếp khởi tạo một hàm scala từ java, mã kết quả là khá xấu xí. Nếu bạn chỉ muốn truy cập một số mã scala đôi khi, nó là ok. Nhưng nếu bạn cung cấp một API java, bạn sẽ có một mã đẹp hơn bằng cách thêm một số giao diện cấp cao hơn và các phương thức scala được thiết kế cho API java. Nó sẽ giúp rất nhiều để đối phó với việc thiếu độ phân giải tiềm ẩn ví dụ. – paradigmatic

58

Trong gói scala.runtime, có các lớp trừu tượng có tên là AbstractFunction1 và cứ thế cho các hành vi khác. Để sử dụng chúng từ Java bạn chỉ cần phải ghi đè apply, như thế này:

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

Nếu bạn đang ở trên Java 8 và muốn sử dụng Java 8 cú pháp lambda cho điều này, hãy kiểm tra https://github.com/scala/scala-java8-compat.

+2

Cảm ơn! Câu trả lời của bạn là hữu ích nhất! Cố gắng thực hiện Function1, Java nói với tôi rằng tôi phải thực hiện hàng tá phương thức - Function1 có các chuyên môn hóa cho một số kiểu nguyên thủy. –

+1

Điều này * là * câu trả lời bạn đang tìm kiếm :) Ngoài ra, hãy kiểm tra http://twitter.github.io/scala_school/java.html – alexr

+0

Lưu ý rằng nếu bạn đang biên soạn cho Android hoặc iOS, bạn sẽ không thể để sử dụng scala-java8-compat vì retrolambda chỉ có thể sửa đổi những gì bạn có tệp nguồn. Nguồn cũng không có các lớp cầu lambda (JFunction0 vv) vì chúng được tạo mã từ sbt trực tiếp tới các tệp .class. Có thể có một cách để bao gồm các tệp .class trong dự án của bạn, nơi retrolambda có thể sửa đổi chúng, nhưng tôi đã không đầu tư nỗ lực để tìm ra điều đó. – voxoid

0

Đây là nỗ lực của tôi tại một giải pháp, một thư viện nhỏ: https://github.com/eirslett/sc8

Bạn quấn lambda Java 8 của bạn trong F (...) và sau đó nó được chuyển đổi sang một hàm Scala.

Các vấn đề liên quan