第一个称为abstract type member,第二个与Java 泛型非常相似,但并不完全相同。这是实现同一目标的两种不同方式。正如 Martin Odersky 在his 采访中解释的那样,同时拥有抽象类型成员和泛型类型参数的一个原因是正交性:
一直有两个抽象概念:参数化
和抽象成员。在 Java 中,你也有两者,但这取决于
你在抽象什么。在 Java 中,你有抽象方法,但是
您不能将方法作为参数传递。你没有摘要
字段,但您可以将值作为参数传递。同样你
没有抽象类型成员,但您可以将类型指定为
范围。所以在Java中你也有这三个,但是有一个
区分您可以将什么抽象原则用于什么
各种东西。你可以争辩说这种区别是公平的
随意。
我们在 Scala 中所做的就是尝试更加完整和正交。我们
决定对所有三种类型都有相同的构造原则
的成员。所以你可以有抽象字段和值
参数。您可以将方法(或“函数”)作为参数传递,或者
你可以抽象它们。您可以将类型指定为参数,或者
你可以抽象它们。我们从概念上得到的是,我们
可以根据另一个建模一个。至少在原则上,我们可以
将各种参数化表达为面向对象的一种形式
抽象。所以在某种意义上你可以说 Scala 是一个更正交的
和完整的语言。
他还描述了抽象类型成员和泛型类型参数之间可以在实践中出现的区别:
但在实践中,当您使用类型参数化与许多
不同的东西,它会导致参数爆炸,通常,
更重要的是,在参数范围内。在 1998 年的 ECOOP 上,金布鲁斯,
Phil Wadler,我有一篇论文,我们展示了随着你的增加
你不知道的事情的数量,典型的程序会增长
二次方。所以有很好的理由不做参数,
但是要有这些抽象的成员,因为他们不给你这个
二次爆炸。
我认为 Bill Veners(ScalaTest 的创建者)的given 是一个很好且简单的例子:
// Type parameter version
trait FixtureSuite[F] {
// ...
}
和
// Type member version
trait FixtureSuite {
type F
// ...
}
在任何一种情况下,F 都是要传递给测试的夹具参数的类型,套件子类将使其具体化。下面是一个具体的测试套件示例,需要使用类型参数方法将 StringBuilder 传递到每个测试中:
// Type parameter version
class MySuite extends FixtureSuite[StringBuilder] {
// ...
}
下面是一个具体的测试套件示例,需要使用抽象类型成员方法将 StringBuilder 传递到每个测试中:
// Type member version
class MySuite extends FixtureSuite {
type F = StringBuilder
// ...
}
例如,如果您想将三个不同的夹具对象传递给测试,您可以这样做,但您需要指定三种类型,每个参数一个。因此选择了类型参数方法,您的套件类最终可能看起来像这样:
// Type parameter version
class MySuite extends FixtureSuite3[StringBuilder, ListBuffer, Stack] with MyHandyFixture {
// ...
}
而使用类型成员方法,它看起来像这样:
// Type member version
class MySuite extends FixtureSuite3 with MyHandyFixture {
// ...
}
因此,这显示了实现出色模块化抽象目标的两种方法。有关此主题的更多信息,请参阅 this 关于可扩展组件的传奇论文