【问题标题】:Good style: Type aliases vs subclassing empty classes/traits好的风格:类型别名与子类化空类/特征
【发布时间】:2012-10-17 20:47:26
【问题描述】:

Twitter Effective Scala - Type Aliases,他们说:

当别名可以使用时不要使用子类化。

trait SocketFactory extends (SocketAddress => Socket)

SocketFactory 是一个产生 Socket 的函数。使用类型 别名

type SocketFactory = SocketAddress => Socket

更好。我们现在可以为类型的值提供函数文字 SocketFactory 也使用函数组合: val addrToInet: SocketAddress => Long val inetToSocket: Long => Socket

val factory: SocketFactory = addrToInet andThen inetToSocket

请注意,类型别名不是新类型——它们等同于在语法上用别名代替其类型。

我们正在谈论的事情是:

trait Base
trait T1 extends Base // subclassing
type T2 = Base        // type alias

当类/特征有主体或存储信息时,显然不能使用类型别名作为替代。

因此使用类型别名 (T2) 而不是使用特征或类 (T1) 进行扩展具有以下优点:

  1. 正如上面所说,我们可以使用函数字面量进行组合。
  2. 我们不会生成 .class 文件,编译器要做的事情会更少(理论上)。

但是,它有以下缺点:

  1. 要在同一个命名空间(包)中可用,您需要在包对象中定义类型,该对象可能位于使用站点的另一个文件中。
  2. 在 Eclipse 中,您不能在别名上跳转 'Open Type' ctrl-shift-T,但可以在 Eclipse 中打开声明 (F3)。这可能会在未来得到解决。
  3. 您不能使用其他语言的类型别名,例如 Java。
  4. 如果类型别名是参数化的,则擦除会阻止模式匹配以与特征相同的方式工作。

第四点对我来说是最严重的:

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

编译器对第一个模式匹配发出警告,这显然不能按预期工作。

所以,最后,这个问题。使用类型别名而不是扩展特征/类还有其他优点或缺点吗?

【问题讨论】:

  • 模式匹配对我来说很有意义,尽管我确实不得不考虑一分钟。 T3 作为类型别名意味着您可以在任何地方用 T1[Any] 替换 T3。所以第一场比赛真的是c match { cast t: T1[Any] => "T3"; ... }cC2,它是 T1[Any] 的子类型(通过 T2)。所以 not 匹配模式实际上会更令人惊讶。

标签: scala


【解决方案1】:

我认为它们的关键实际上是类型别名和特征确实不同。不同之处的列表还在继续:

  1. 简写语法适用于类型别名(例如,x => x+7 将用作 type I2I = Int => Int)而不是特征。
  2. 特征可以携带额外的数据,类型别名不能。
  3. 隐式适用于类型别名,但不适用于特征。
  4. Traits 提供类型安全/匹配的方式是类型别名所不具备的。
  5. 类型别名对于在子类中被覆盖有严格的规定;取而代之的是同名特征阴影(什么都行)。

等等。

这是因为您在这两种情况下所做的事情截然不同。类型别名只是一种表达方式,“好吧,当我输入 Foo 时,我实际上是指 Bar。它们是一样的。明白吗?很酷。”完成此操作后,您可以随时随地将名称 Foo 替换为 Bar。唯一的限制是一旦你决定了一个类型是什么你不能改变主意

另一方面,Traits 创建了一个全新的界面,它可以扩展 trait 扩展的内容,也可以不扩展。如果不是,它仍然是一个标记,表明这是它自己的实体类型,可以进行模式匹配,使用“isInstanceOf”进行测试等等。

所以,既然我们已经确定它们是真正不同的,那么问题是如何使用它们。答案很简单:如果你喜欢现有的类,只是不喜欢它的名字,那就使用类型别名。如果您想创建自己的不同于其他事物的新实体,请使用特征(或子类)。前者主要是为了方便,而后者是为了增加类型安全性或功能。我不认为任何说使用一个代替另一个的规则真正抓住了重点——了解两者的特性,并在你想要的特性时使用它们。

(然后是存在类型,它提供与泛型类似的功能......但让我们把它留给另一个问题。)

【讨论】:

  • 所以,您是说两者完全不同,如果不充分了解两者之间的行为差​​异,不应该遵循 twitter 风格指南中的建议吗?我也是这么想的。
  • @MatthewFarwell - 是的,这是一个公平的总结。
【解决方案2】:

它们的不同之处在于类型别名定义了类型相等关系(即 T1 <: t2 t1>: T2),而特征扩展定义了严格的子类型关系(即 T1 <: t2> : T2))。明智地使用它们。

【讨论】:

    猜你喜欢
    • 2016-07-31
    • 1970-01-01
    • 1970-01-01
    • 2015-11-20
    • 2013-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-31
    相关资源
    最近更新 更多