我知道那段代码! :)
所以让我们确保你理解 <:
的意思,以防万一。 A <: B
意味着 A
必须是 B
的子类型,换句话说,A
的每个实例也将是 B
的实例(但反之则不然)。
例如,我们知道每个Java类都是<: Object
(如 String <: Object
)。
接下来是为什么 type ValueChanged <: Event
。这通常在蛋糕模式中找到,但我会跳过对此的解释(课程提到了蛋糕模式,并提供了一个链接,如果需要可以查看)。
这意味着对于任何扩展 SwingApi
的内容,类型 ValueChanged
必须是 Event
的子类型。这使得可以在声明为 ValueChanged
类型的任何内容上调用 Event
方法,而无需预先知道确切的类型是什么。
这与下一个用法类似:
type TextField <: {
def text: String
def subscribe(r: Reaction): Unit
def unsubscribe(r: Reaction): Unit
}
我们在这里声明 TextField
应该具有那些方法,因此当我们获取到类型为 TextField
的东西(比如由 ValueChanged
提取器返回的东西)时,我们可以在其上调用这些方法。我们可以编写如下代码:
trait MyStuff extends SwingApi {
def subscribeTo(event: ValueChanged) = event match {
case ValueChanged(textField) => textField.subscribe(myReaction)
}
def myReaction: Reaction
}
此时,无论是SwingApi
还是MyStuff
都不知道将用于ValueChanged
或TextField
的类型,但是它们可以在正常代码中使用。
关于type
声明,一个常被忽视的有趣事实是它们可以被类覆盖。也就是说,我可以这样写:
class SwingImpl extends SwingApi {
class TextField {
def text: String = ???
def subscribe(r: Reaction): Unit = ???
def unsubscribe(r: Reaction): Unit = ???
}
}
最后,您可能会想知道这有什么用处。我来举一个例子。自然地,您希望生产代码在屏幕上显示图形元素等内容,也许您可以编写一个单独的类,在Web服务器上实现它。但是,我认为本课程充分利用了这一点,您可以将实现这些组件的类编写为测试类,以验证与这些组件的交互是否正确。
也就是说,您可以拥有扩展SwingApi
并在桌面上显示其内容的SwingImpl
,还可以拥有扩展SwingApi
但只用于验证操作是否正确的SwingTest
。
<:
,还是只是一般地了解<:
的用法? - DaoWen