【问题标题】:Scala Stackable Trait and Self Type Incompatible TypeScala Stackable Trait 和 Self 类型不兼容的类型
【发布时间】:2014-03-30 16:24:31
【问题描述】:

我有一个名为 Mutatable 的特性,它会吐出一个实现类的修改副本。我还有一个我想在它上面叠加的特征,称为 CostedMutatable,它可以跟踪这样做的成本。 applyMutation 方法返回一个选项,因为稍后我想在特定突变不适用的情况下返回 None。

一个仅适用于 Ints 的简单版本(并通过添加新数字“改变”它们)如下所示:

trait Mutatable[M] {

  def applyMutation(mut : M) : Option[this.type]
}

trait CostedMutatable[M] extends Mutatable[M]{

  var cost : Int = _
  def getCostFor(mut : M): Int

  abstract override def applyMutation(mut : M) : Option[this.type] = {
    cost += getCostFor(mut)
    applyMutation(mut)
  }
}

object Example extends App {

  case class Mutation(x: Int)

  class Test(s: Int) extends Mutatable[Mutation] {
    val start = s
    override def applyMutation(mut: Mutation): Option[Test] 
         = Some(new Test(s+mut.x))
  }

  class CostTest(s: Int) extends Test(s) with CostedMutatable[Mutation] {
    override def getCostFor(mut: Mutation): Int = 2
  }

  val testCost = new CostTest(5).cost
}

问题是,这不会编译。我在编译时收到以下错误:

Error:(23, 18) overriding method applyMutation in trait Mutatable of type (mut: Example.Mutation)Option[Test.this.type];
 method applyMutation has incompatible type
    override def applyMutation(mut: Mutation): Option[Test] = Some(new Test(s+mut.x))
                 ^

除了编译器错误之外,我还想到另一个问题:我是否以正确的方式接近这个问题?我应该改用 F 有界类型吗? (我需要每个新的实现类从 applyMutation 返回具体实现类的新副本。)

提前致谢。

【问题讨论】:

    标签: scala typing


    【解决方案1】:

    this.type 是一种类型,其唯一实例是 thisNothing。当方法返回this.type 时,唯一允许的返回值是this。在Test 类中,applyMutation 不返回this,而是一个全新的Test,它不是this.type 的实例。这就是代码不进行类型检查的原因。

    我认为您真正想要做的是声明 applyMutation 返回与this 相同类的值。这样做确实需要 F-Bounded 多态性。这是您的代码的重写版本:

    trait CostedMutatable[+A <: CostedMutatable[A, M], M] extends Mutatable[A, M] {
    
      var cost : Int = _
      def getCostFor(mut : M): Int
    
      abstract override def applyMutation(mut: M): Option[A] = {
        cost += getCostFor(mut)
        super.applyMutation(mut)
      }
    }
    
    object Example extends App {
    
      case class Mutation(x: Int)
    
      class Test(s: Int) extends Mutatable[Test, Mutation] {
        val start = s
        override def applyMutation(mut: Mutation): Option[Test] 
             = Some(new Test(s+mut.x))
      }
    
      class CostTest(s: Int) extends Test(s) with CostedMutatable[CostTest, Mutation] {
        override def getCostFor(mut: Mutation): Int = 2
      }
    
      val testCost = new CostTest(5).cost
    }
    

    【讨论】:

    • 我将 Mutatable 的签名更改为 trait Mutatable[+A, M],然后添加到您的代码更改中,现在一切都可以编译了。谢谢!顺便说一句,您知道this.type 是否仅在流畅的方法链接中有用?
    • 除了流畅的链接之外还有一些其他的用途。例如,this.type 在处理依赖于路径的类型时很有用。 Outerinstance1.innerclass 是与 Ounterinstance2.innerclass 不同的类型,如果没有 this.type,您可能会遇到本来可以避免的类型错误。
    • 当我开始尝试依赖路径的类型时,我会将其归档。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    • 1970-01-01
    • 2015-08-11
    相关资源
    最近更新 更多