使用子类的类型实现特征方法

时间:2019-07-11 19:20:54

标签: scala inheritance traits

我希望特征Foo提供一种transform方法,该方法将对其应用一个功能。另外,我想强制实现类具有一个increment方法,该方法也将以某种方式转换对象。天真的解决方案:

trait Foo {
  def transform(fun: Foo => Foo): Foo = fun(this)
  def increment(n: Int): Foo
}

case class A(a: Int) extends Foo {
  // expecting available: transform(fun: A => A): A
  // to be implemented: increment(n: Int): A
  ...
}

以上操作均无效...继承的transform仍然希望Foo => Foo,而不是A => A,并且increment仍然希望返回Foo,而不是{{ 1}}。

再尝试一次

A

trait Foo { def transform[C <: Foo](fun: C => C): C = fun(this.asInstanceOf[C]) def increment[C <: Foo](n: Int): C } case class A(a: Int) extends Foo { def increment(n: Int) = A(a + n) } 无法编译-仍会抱怨签名。

取出A函数,转换就可以了。但是increment看起来有点不安全。另外,我需要为asInstanceOf明确提供类型参数:

transform

我想知道是否有一种聪明的方法来完成它。

1 个答案:

答案 0 :(得分:5)

获得所需内容的最直接方法是将类型参数上移至trait声明。得到trait Foo[C]{...}。但是,在copy中使用transform仍然行不通,因为Foo特性不知道任何扩展特性。您可以使用self typing为它提供更多信息:

trait Foo[C] {
  this: C =>
    def transform(fun: C => C): C = fun(this)
    def increment(n: Int): C
}

case class A(a: Int) extends Foo[A] {
  def increment(n: Int) = A(a + n)
}

A extends Foo[A]在这里使用有点尴尬,但是它可以工作,因为现在扩展Foo时,它将类型信息提供回特征。但是,这仍然有点尴尬。事实证明,存在一种称为type classes的技术,我们可以在这里使用它来潜在地改善性能。首先,设置您的特质。在类型类中,每种类型恰好有一个trait的实现,因此每种方法还应采用您要对其进行操作的实例:

trait Foo[C] {
  def transform(c: C)(f: C => C): C
  def increment(c: C, inc: Int): C
}

接下来,在伴随对象中,为您关心的类型设置typeclass实例:

case class A(a: Int)

object Foo {
  implicit val ATransform = new Foo[A] {
    def transform (base: A)(f: A => A) = f(base)
    def increment(base: A, inc: Int) = A(base.a+inc)
  }

  //Convenience function for finding the instance for a type.
  //With this, Foo[A] is equivalent to implicitly[Foo[A]]
  def apply[C](implicit foo: Foo[C]) = foo
}

现在我们可以如下使用类型类:

val b = A(3)
Foo[A].transform(b)(x=>x.copy(a=x.a+1)) //A(4)
Foo[A].increment(b,5) //A(8)