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

时间:2011-06-26 09:52:47

标签: java scala interface

据我了解,当它们最终出现时,我们将能够用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")
}

6 个答案:

答案 0 :(得分:11)

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

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

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

答案 1 :(得分:4)

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

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

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

答案 2 :(得分:2)

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

使用隐式转换添加addSamListener之类的内容。

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

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

答案 3 :(得分:1)

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

答案 4 :(得分:1)

答案取决于你所要求的......

如果调用需要A => B的Scala代码,那么这会消除{em>已经已经 SAM类型的Function1[A,B],因此一旦Java需要进一步的工作终于获得了关闭。

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

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

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

答案 5 :(得分:1)

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

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