Scala-用边界覆盖类型成员

时间:2018-12-20 20:40:49

标签: scala generics traits type-bounds type-members

Scala代码中的特征层次结构存在以下问题:

首先,我具有一个基本特征MyTrait[A],其定义如下:

trait MyTrait[A] {
  def v1: A
}

然后是带有类型成员的特征Base的定义:

trait Base[A] {
  type T <: MyTrait[A]
  val baseV: T
}

最后,特征Gen会覆盖Base的类型成员。

trait Gen[A, X <: MyTrait[A]] extends Base[A] {
  type T = X
}

问题在于,在Gen特性中,类型成员的界限似乎丢失了。可以通过以下测试证明这一点:

编译:

trait Test1 {
  val x: Base[_]
  println(x.baseV.v1)
}

不编译(value v1 is not a member of Test2.this.x.T):

trait Test2 {
  val x: Gen[_, _]
  println(x.baseV.v1)
}

我想知道这是语言的限制还是有解决方法。关于stackowerflow(12)的类似主题的问题似乎集中在与我的问题不同的方面,而我真的很茫然,因为我无法在Scala中找到有关此类行为的更多信息。

此问题的标量代码模板可以在scastie

上找到

1 个答案:

答案 0 :(得分:5)

这有效:

trait Test2 {
  val x: Gen[A, X] forSome { type A; type X <: MyTrait[A] }
  println(x.baseV.v1)
}

我相信问题是

Gen[_, _]

Has to mean

Gen[_ >: Nothing <: Any, _ >: Nothing <: Any]

相同
Gen[A, X] forSome { type A; type X }

也就是说,即使Gen的边界表示X <: MyTrait[A],通配符也不会继承该边界。您可以在这里看到类似的问题:

trait Data { def data: String }
trait Box[A <: Data] { def data: A }
def unbox(b: Box[_]): String = b.data.data // nope; the wildcard is not <: Data

我们可以将边界显式添加到通配符。但是,由于第二个通配符的界限取决于第一个通配符,因此我们被迫将扩展的forSome语法用于存在性,因此我们可以命名A并使用两次。

Gen[A, _ <: MyTrait[A]] forSome { type A }

我选择将所有内容都放入存在子句中,该子句是等效的:

Gen[A, X] forSome { type A; type X <: MyTrait[A] }

您也可以使用

Gen[_, _ <: MyTrait[_]]

但这并不等效,因为它不涉及left和right参数。如果Gen[A, _]A之外还包含MyTrait[A],则使用x: Gen[_, _ <: MyTrait[_]]将使用不兼容的类型呈现“裸”值和“包裹”值。