Kom*_*owy 3 inheritance scala 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
...
}
Run Code Online (Sandbox Code Playgroud)
上面将无法工作......继承transform仍预计Foo => Foo,不A => A和increment仍想返回Foo,没有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)
}
Run Code Online (Sandbox Code Playgroud)
A 将无法编译-仍会抱怨签名。
取出increment功能,进行转换即可。但是asInstanceOf看起来有点不安全。另外,我需要显式提供type参数给transform:
val a = A(1)
a.transform[A](x => x.copy(x.a + 1)) // returns A(2)
Run Code Online (Sandbox Code Playgroud)
我想知道是否有一种聪明的方法来完成它。
获得所需内容的最直接方法是将类型参数上移至trait声明。那给了trait Foo[C]{...}。但是,copy在您的代码中transform仍然无法使用,因为Foo特质对扩展它的任何内容一无所知。您可以通过自我输入为它提供更多信息:
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)
}
Run Code Online (Sandbox Code Playgroud)
在A extends Foo[A]这里使用有点尴尬,但是它可以工作,因为现在扩展时Foo,它将类型信息提供回特征。但是,这仍然有点尴尬。事实证明,有一种称为类型类的技术,我们可以在这里使用它来潜在地改善性能。首先,设置您的特质。在类型类中,每种类型恰好有一个trait的实现,因此每种方法还应采用您要对其进行操作的实例:
trait Foo[C] {
def transform(c: C)(f: C => C): C
def increment(c: C, inc: Int): C
}
Run Code Online (Sandbox Code Playgroud)
接下来,在伴随对象中,为您关心的类型设置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
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以使用类型类,如下所示:
val b = A(3)
Foo[A].transform(b)(x=>x.copy(a=x.a+1)) //A(4)
Foo[A].increment(b,5) //A(8)
Run Code Online (Sandbox Code Playgroud)