在Twitter Effective Scala - Type Aliases中,他们说:
当别名发生时,不要使用子类化。
trait SocketFactory extends (SocketAddress => Socket)
SocketFactory是一个生成Socket的函数。使用类型 别名
type SocketFactory = SocketAddress => Socket
更好。我们现在可以为类型的值提供函数文字 SocketFactory还使用函数组合: val addrToInet:SocketAddress => Long val inetToSocket:Long =>插座
val factory: SocketFactory = addrToInet andThen inetToSocket
请注意,类型别名不是新类型 - 它们等同于在语法上用别名替换其类型。
我们谈论的是:
trait Base
trait T1 extends Base // subclassing
type T2 = Base // type alias
显然,当类/特征具有正文或存储信息时,您不能使用类型别名作为替换。
因此,使用类型别名(T2)而不是使用特征或类(T1)进行扩展具有以下优点:
但是,它有以下缺点:
第四点对我来说是最严重的:
trait T1[T]
trait T2 extends T1[Any]
type T3 = T1[Any]
class C2 extends T2
val c = new C2
println("" + (c match { case t: T3 => "T3"; case _ => "any" }))
println("" + (c match { case t: T2 => "T2"; case _ => "any" }))
这会产生:
T3
T2
编译器会发出有关第一个模式匹配的警告,这显然不能按预期工作。
所以,最后,问题。使用类型别名而不是扩展特征/类是否有任何其他优点或缺点?
答案 0 :(得分:8)
我认为他们的关键实际上是类型别名和特征真的不同。差异列表一直在继续:
x => x+7
可用作type I2I = Int => Int
)而不是特征。等。
这是因为在这两种情况下你做了截然不同的事情。类型别名只是一种说法,“好吧,当我输入Foo时,我实际上是指Bar。他们是相同的。知道了吗?很酷。”执行此操作后,您可以随时随地将Foo
替换为Bar
。唯一的限制是,一旦你决定了什么类型,你就无法改变主意。
所以,现在我们已经确定他们真的不同,问题是如何使用每个。答案很简单:如果您喜欢现有的类,除了您不喜欢名称,请使用类型别名。如果要创建与其他事物不同的新实体,请使用特征(或子类)。前者主要是为了方便,后者是为了增加类型安全性或能力。我不认为任何规则说使用一个而不是另一个真正抓住了这一点 - 理解两者的特征,并在那些是你想要的特征时使用它们。
(然后是存在类型,它们提供与泛型类似的能力......但是让我们留下另一个问题。)
答案 1 :(得分:2)
它们的不同之处在于类型别名定义了类型相等关系(即T1<:T2&& T1>:T2),而特征扩展定义了严格的子类型关系(即.T1<: T2&&!(T1>:T2))。明智地使用它们。