by Bill Venners
October 7, 2009
http://www.artima.com/weblogs/viewpost.jsp?thread=270195
摘要:
在这篇博客中,作者试图回答Scala编程中一个共同的问题:在Scala API设计时,什么时候用泛型参数,什么时候用抽象类型成员。
---------------------------------------------------------------------------------------------
学习Scala建一个抽象类型模型的时,一个普遍的问题是如何决定使用泛型参数,还是抽象类型成员。对于不熟悉这两则区别的人来说,Scala的泛型参数有点像Java的泛型参数,只是Java用尖括号,而Scala用方括号。
Java泛型
interface Collection<T> {
// ...
}
Scala泛型使用方括号
// Type parameter version trait Collection[T] { // .. }
Scala的抽象类型成员(Abstract type members)没用和Java等同的。 两个语言中,类,接口(Java),特征(Scala)都可以有方法和字段作为成员。Scala的类或特征(trait)可以有类型成员,下面例子是抽象类型成员:
// Type member version trait Collection { type T // ... }
这两种情况,抽象类型都可以在子类中具体化。下面的例子中子类用String具体化了类型参数T:
// Type parameter version
trait StringCollection extends Collection[String] { // ... }
或者在子类内部具体化类型参数T:
// Type member version trait StringCollection extends Collection { type T = String // ... }
这看上去,事实上也是,有两种不同的方法达到同一个目的。那么我们怎么做选择呢?我在采访Martin Odersky(Scala创始人)的时候问了这个问题,他最先解释了同时有这两种方法是正交的。
参数化和抽象成员。Java也有可以同时有这两个,但这取决于你想抽象什么。
用Java你可以有抽象方法,但你不能将方法当作参数。你不能有抽象字段,但可以将传递参数。简单将你可以没用抽象类型成员,但你可以将具体的类型当参数。所以用Java你也可以有这三种方式,只是你在做不同类型的事情时的抽象原则有所不同。你可以说这些差异是简直是专制。
我们试图让Scala在抽象方面变得完全和正交。我们决定对所有这三种成员(类型,字段,方法)的抽象有统一的构造原则。你可以像值参数那样有抽象字段。你可以像参数一样传递方法(或叫函数),或者你能抽象他们。你可以像参数那样具体化类型,或者抽象他们。我们可以在概念上为其他建立一个模型(we can model one in terms of the other)。至少在原则上,我们能对每个不同的参数化做统一的OO抽象。所以在一定意义上说,Scala是更充分和正交(orthogonal )的语言。
他同时解释了抽象类型成员和泛型参数在实际应用中的不同:
// Type parameter version
trait FixtureSuite[F] {
// ...
}
// Type member version
trait FixtureSuite {
type F
// ...
}
// Type parameter version
class MySuite extends FixtureSuite[StringBuilder] {
// ...
}
// Type member version
class MySuite extends FixtureSuite {
type F = StringBuilder
// ...
}