代码之家  ›  专栏  ›  技术社区  ›  a7emenov

scala-用边界重写类型成员

  •  4
  • a7emenov  · 技术社区  · 6 年前

    我对scala代码中的特征层次结构有以下问题:

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

    trait MyTrait[A] {
      def v1: A
    }
    

    接着是一个特征的定义。 Base 对于类型成员:

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

    最后,一个特点 Gen 哪些超越 基地 的类型成员。

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

    问题在于 消息 特征似乎丢失了类型成员的边界。这可以通过以下测试来证明:

    编译:

    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中找不到关于这种行为的很多信息。

    这个问题的scala代码模板可以在 scastie

    1 回复  |  直到 6 年前
        1
  •  5
  •   HTNW    6 年前

    这工作:

    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[_]]
    

    但这不是等价的,因为它不涉及左右参数。如果 Gen[A, _] 包含一个 除了 MyTrait[A] 然后使用 x: Gen[_, _ <: MyTrait[_]] 将使用不兼容的类型呈现“bare”值和“wrapped”值。