Scala ActionListener / 匿名函数类型不匹配

6

我试图实现与http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-6中的高阶函数示例类似的代码。

val button = new JButton("test")
button.addActionListener{ e:ActionEvent => println("test") }
add(button)

导致以下结果。
error: type mismatch;
found   : (java.awt.event.ActionEvent) => Unit 
required: java.awt.event.ActionListener
   button.addActionListener{ e:ActionEvent => println("test") }
                                           ^

至少在我的系统上,使用Scala编译器版本2.7.6.final是正确的。我能够以Java风格显式地实现匿名ActionListener来达到我想要的效果。

button.addActionListener( new ActionListener() {
  def actionPerformed(e:ActionEvent) { println("test") }
})

据我了解,Scala应该能够使用鸭子类型来渲染这个ActionListener的显式实现不必要;那么为什么在这里它没有起作用呢?在这一点上,我几乎没有任何实际经验。
2个回答

11

鸭子类型与你的代码无关,导致代码不起作用的原因是因为Scala的类型系统默认情况下不提供接口类型和函数类型之间的隐式转换。但是,如果定义了以下隐式转换,则代码可以正常工作。

implicit def toActionListener(f: ActionEvent => Unit) = new ActionListener {
  def actionPerformed(e: ActionEvent) { f(e) }
}

这种隐式转换提供了从(ActionEvent => Unit)到ActionListener的转换。


1
并非狂热支持,但值得注意的是,Groovy和各种Java闭包提案都包括从适当函数类型到单方法接口的隐式转换。这对于像Runnable或Comparable这样的接口非常方便。在Scala中,您需要手动执行此转换,或者依赖于 pimped 库来执行它。 - Dave Griffith
1
@Dave 是的...如果类型不匹配,自动转换它。我认为这就是为什么Scala没有选择这种方式的明显原因。 - Daniel C. Sobral
哦,当然。这只有在Groovy和Java中才有意义,因为它们没有包含任何好的方法来扩展现有库,所以它们需要这种自动转换来使与遗留Java一起工作变得不痛苦。Scala有用于遗留库的pimps,并且新库应该(并正在)编写以支持函数形式而不是单方法接口。 - Dave Griffith

4

Scala并非鸭子类型(duck-typed)语言。它具有可选的、显式的结构化类型,但这与你的代码无关。

你的代码无法正常工作,是因为JButton.addActionListener期望一个ActionListener作为其参数,而不是一个函数。即使Scala是鸭子类型语言,你也不能只传递一个函数,因为在ActionListener上可用的相同方法在函数上不可用。

请注意,文章中说:“让我们假设我们可以重写Swing以充分利用Scala的语法”,即文章并没有声称向你展示实际可用的代码。

然而,Scala确实有自己的swing包(scala.swing),其中包含的类比其Java等效类更加“Scala风格”。


啊,那篇文章中的引用确实很恰当。我有一种感觉,在公开场合提问肯定会暴露出我基本的阅读理解能力不足...不过,你的回答很有启发性。谢谢。 - PeterT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接