通常使用Scala闭包实现Java Single-Abstract-Method接口?

据我了解,当它们最终出现时,我们将能够用Java闭包代替等效的单方法接口。 是否有标准的Scala习惯用法 – 使用Scala闭包实现Java Single Abstract Method接口?

理想情况下,我希望以下内容能够实现自动化

test("Closure") { var event: PropertyChangeEvent = null var label = new JLabel() label.addPropertyChangeListener( {e: PropertyChangeEvent => event = e} ) label.setText("fred") event.getNewValue should be ("fred") } 

1月份对此进行了长时间的讨论。

http://www.scala-lang.org/node/8744

这个想法很受欢迎,甚至比这个提议更进一步。 但是魔鬼在细节中,原型实现可能会在此提案中找到其他问题。

在scala中,您可以尝试隐式转换:

 implicit def func2PropertyChangeListener( f: ProperyChangeEvent => Unit ) = new PropertyChangeListener { def propertyChange( evt: PropertyChangeEvent ) = f(evt) } 

然后你只需要导入方法,你可以直接在预期的PropertyChangeListener任何地方传递匿名函数。

请参阅http://www.tikalk.com/incubator/blog/simulating-sam-closures-scala

使用隐式转换添加类似addSamListener

您需要编写转换,但不需要在任何地方导入它(只是添加addSamListener的一个隐式转换)。

如果所有接口共享相同的方法,则可以使用结构类型。

为每个旧的Java接口执行此操作的唯一方法是代码生成。 由于Java解析器随时可用,您只需要提取接口名称和方法签名,这应该很容易实现,并且只需要运行一次。 事实上,这听起来像一个很好的星期天晚上项目。 其他人也可能从中受益……

答案取决于你究竟要问的是什么……

如果调用期望A => B Scala代码,那么这会消耗到已经是SAM类型的Function1[A,B] ,因此一旦Java最终获得闭包,就不需要进一步的工作了。

(至少我们分散了Duke Nukem Forever,它首先到达了我们的显示器……)

如果Scala调用需要SAM类型的Java代码,那么一种技术是提供从Scala函数到预期接口的隐式转换(如范例所示)。 未来版本的Scala很可能会自动处理这个问题(正如反语所指出的那样)

另外值得一提的是Scala的专业化,它可以产生具有多个重载版本的函数。 据我所知,Java不会提供任何这种性质,因此interop可能必须通过AbstractFunctionN类型之一。

我不能完全重现我想要的语法,但我可能会通过使用具有所需类型的类型参数的方法和块的参数的方法来接近:

  test("PropertyChangeListener") { var event: PropertyChangeEvent = null val label = new JLabel() label.addPropertyChangeListener(SAM[PropertyChangeListener, PropertyChangeEvent] { event = _ }) label.setText("fred") event.getNewValue should be ("fred") } test("Runnable") { var sideEffect: Any = null val r: Runnable = SAM[Runnable] { sideEffect = "fred" } r.run sideEffect should be ("fred") } test("Callable") { val c: Callable[String] = SAM[Callable[String]] { "fred" } c.call should be ("fred") } def SAM[T](block: => Any)(implicit classManifest: ClassManifest[T]): T = { java.lang.reflect.Proxy.newProxyInstance( getClass.getClassLoader, Array(classManifest.erasure), new InvocationHandler { def invoke(proxy: Object, method: Method, args:Array[AnyRef]) = { var result = block block.asInstanceOf[AnyRef] } }).asInstanceOf[T] } def SAM[T, A](block: Function1[A,Any])(implicit classManifest: ClassManifest[T]): T = { java.lang.reflect.Proxy.newProxyInstance( getClass.getClassLoader, Array(classManifest.erasure), new InvocationHandler { def invoke(proxy: Object, method: Method, args:Array[AnyRef]) = { var result = block(args(0).asInstanceOf[A]) result.asInstanceOf[AnyRef] } }).asInstanceOf[T] }