抽象类型与类型参数

Jay*_*nha 21 scala abstract-type type-parameter

在哪些情况下,抽象类型应优先于类型参数?

Von*_*onC 19

要添加我之前关于抽象类型与参数的答案,您还有JESSE EICHAR最近的博客文章(2010年5月3日),突出了一些主要差异:

trait C1[A] {
  def get : A
  def doit(a:A):A
}
trait C2 {
  type A
  def get : A
  def doit(a:A):A
}
Run Code Online (Sandbox Code Playgroud)

C2情况下,参数是"埋"(作为内部抽象类型).
(除了反义词之外,它实际上并没有被埋没,见下文)

而使用泛型类型时,会明确提及该参数,从而帮助其他表达式知道它们应该使用哪种类型


所以(C1:参数):

//compiles
def p(c:C1[Int]) = c.doit(c.get)
Run Code Online (Sandbox Code Playgroud)

它编译,但你明确暴露了A你想要使用的' '类型.

并且(C2:抽象类型):

// doesn't compile
def p2(c:C2) = c.doit(c.get)
<console>:6: error: illegal dependent method type
       def p2(c:C2) = c.doit(c.get)
              ^
Run Code Online (Sandbox Code Playgroud)

它不会编译因为' A'在p2定义中从未被提及过,所以doit在编译类型中不知道它应该返回什么.


当使用抽象类型希望避免任何"类型泄漏"到接口时(即想要暴露A实际上是什么),您可以指定一个非常通用的类型作为p2的返回:

// compiles because the internals of C2 does not leak out
def p(c:C2):Unit = c.doit(c.get)
Run Code Online (Sandbox Code Playgroud)

或者你可以直接在doit函数中"修复"那个类型:
def doit(a:A):Int而不是 def doit(a:A):A,这意味着:
def p2(c:C2) = c.doit(c.get)将编译(即使p2没有提到任何返回类型)


最后(返璞词的注释),你可以指定A任何明确细化C2抽象的参数:

scala> def p2(c:C2 { type A = Int }): Int = c.doit(c.get)
p2: (c: C2{type A = Int})Int
Run Code Online (Sandbox Code Playgroud)

或者通过添加一个类型参数(并用它来改进C2抽象类型!)

scala> def p2[X](c:C2 { type A = X }): X = c.doit(c.get)
p2: [X](c: C2{type A = X})X
Run Code Online (Sandbox Code Playgroud)

建议如此抽象:

  • 当您想要从客户端代码隐藏类型成员的确切定义时,请使用类似于的抽象类型C2(但要警惕使用函数的定义C2)
  • 如果要在子类中重复覆盖类型C2,请使用抽象类型(带有有界类型的抽象)
  • 当你想C2通过traits 混合使用这些类型的定义时,使用抽象类型(AC2你和你的类混合时你不需要处理它:你只混合使用C2)

对于需要简单类型实例化的其余部分,请使用参数.
(如果你知道不需要扩展,但你仍然需要处理几种类型:这就是参数类型的用途)


反语:

主要区别

  • 方差:C2只能是不变的A,
  • 类型成员可以在子类型中有选择地重写的方式(而类型参数必须重新声明并传递给超类型)

(如下图所示:

trait T1 {
  type t
  val v: t
}
trait T2 extends T1 {
  type t <: SomeType1
}
trait T3 extends T2 {
  type t <: SomeType2  // where SomeType2 <: SomeType1
}
class C extends T3 {
  type t = Concrete    // where Concrete <: SomeType2
  val v = new Concrete(...)
}
Run Code Online (Sandbox Code Playgroud)

)

  • 它并没有真正被埋没:`def p2(c:C2 {type A = Int}):Int = c.doit(c.get)`.或者:`def p2 [X](c:C2 {type A = X}):X = c.doit(c.get)`.主要区别是方差(`C2`在'A`中只能是不变的'),以及类型成员可以在子类型中有选择地重写的方式,而类型参数必须重新声明并传递给超类型. (6认同)