scala 抽象类型与类型参数

t1qtbnec  于 2023-04-12  发布在  Scala
关注(0)|答案(1)|浏览(186)

在什么情况下应该优先使用抽象类型而不是类型参数?

nukf8bse

nukf8bse1#

为了补充我之前关于抽象类型与参数的答案,你还有JESSE EICHAR's recent blog post(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
}

C2情况下,参数是“隐藏”的(作为内部抽象类型)。
(除了,正如retronym所说,它实际上并没有被埋葬,见下文)
而对于泛型类型,参数被显式地提到,帮助其他表达式知道它们应该使用什么类型
因此(C1:参数):

//compiles
def p(c:C1[Int]) = c.doit(c.get)

它会编译,但您会显式公开要使用的'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)
              ^

它不会编译,因为p2定义中从未提到'A',所以doit在编译类型时不知道它应该返回什么。
当使用抽象类型 * 和 * 来避免任何“类型泄漏”到接口时(即想要暴露'A'实际上是什么),您可以指定一个非常通用的类型作为p2的返回值:

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

或者你可以直接在doit函数中“修复”这个类型:
def doit(a:A):Int而不是def doit(a:A):A,这意味着:
def p2(c:C2) = c.doit(c.get)将编译(即使p2没有提到任何返回类型)
最后(retronym的注解),您可以通过细化C2抽象参数显式地指定A

scala> def p2(c:C2 { type A = Int }): Int = c.doit(c.get)
p2: (c: C2{type A = Int})Int

或者添加一个类型参数(并使用它来细化C2抽象类型!)

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

所以建议抽象:

*当你想从客户端代码中隐藏类型成员的确切定义时,使用像C2中的抽象类型(但要注意使用C2的函数定义)
*当你想在C2的子类中覆盖类型协变时,使用抽象类型(有界类型抽象)
*当你想通过traits混合这些C2类型的定义时,使用抽象类型(当你将C2与你的类混合时,你不会有'A'来处理:你只混合C2

对于其他需要简单类型示例化的情况,请使用参数。
(if你知道不需要扩展,但你仍然需要处理几种类型:这就是参数类型的用途)
retronym添加:

主要区别
*方差C2只能在A中不变,
*类型成员在子类型中被选择性覆盖的方式(而类型参数必须重新声明并传递给超类型)

(as illustrating here

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(...)
}

Dmytro Mitin在评论中添加了2023:

方差C2只能在A中保持不变

这并不完全正确。
只有C1的变量可以在定义位置和调用位置声明,而C2的变量只能在调用位置声明(如Java中的类型参数)
参见“Scala: Abstract types vs generics
“在子类型中有选择地重写类型成员的方式”
这也不是区别。

trait T1[t] 
trait T2[t <: SomeType1] extends T1[t] 
trait T3[t <: SomeType2] extends T2[t] 
class C extends T3[Concrete]

在2.10+ def p2(c:C2) = c.doit(c.get)编译。
参见this scastie snippet

相关问题