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(1,2)的类似主题的问题似乎集中在与我的问题不同的方面,而我真的很茫然,因为我无法在Scala中找到有关此类行为的更多信息。
此问题的标量代码模板可以在scastie
上找到答案 0 :(得分:5)
这有效:
trait Test2 {
val x: Gen[A, X] forSome { type A; type X <: MyTrait[A] }
println(x.baseV.v1)
}
我相信问题是
Gen[_, _]
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[_]]
将使用不兼容的类型呈现“裸”值和“包裹”值。