使用子类的类型实现特征方法
Implement trait methods using subclasses' types
我想要一个 trait 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
仍然需要 return 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)
}
A
不会编译 - 它仍然会抱怨签名。
取出 increment
函数,转换有效。但是 asInstanceOf
看起来有点不安全。另外,我需要明确地向 transform
:
提供类型参数
val a = A(1)
a.transform[A](x => x.copy(x.a + 1)) // returns A(2)
我想知道是否有一个聪明的方法来完成它。
获得所需内容的最直接方法是将类型参数移至特征声明。这给出了 trait Foo[C]{...}
。然而,在你的 transform
中使用 copy
仍然不起作用,因为 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 的技术,我们可以在这里使用它来潜在地改进事情。首先,你设置你的特质。在一个类型 class 中,每个类型只有一个 trait 的实现,所以每个方法也应该接受你想要操作的实例:
trait Foo[C] {
def transform(c: C)(f: C => C): C
def increment(c: C, inc: Int): C
}
接下来,在伴随对象中为您关心的类型设置 class 类型的实例:
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
}
现在我们可以使用类型class如下:
val b = A(3)
Foo[A].transform(b)(x=>x.copy(a=x.a+1)) //A(4)
Foo[A].increment(b,5) //A(8)
我想要一个 trait 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
仍然需要 return 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)
}
A
不会编译 - 它仍然会抱怨签名。
取出 increment
函数,转换有效。但是 asInstanceOf
看起来有点不安全。另外,我需要明确地向 transform
:
val a = A(1)
a.transform[A](x => x.copy(x.a + 1)) // returns A(2)
我想知道是否有一个聪明的方法来完成它。
获得所需内容的最直接方法是将类型参数移至特征声明。这给出了 trait Foo[C]{...}
。然而,在你的 transform
中使用 copy
仍然不起作用,因为 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 的技术,我们可以在这里使用它来潜在地改进事情。首先,你设置你的特质。在一个类型 class 中,每个类型只有一个 trait 的实现,所以每个方法也应该接受你想要操作的实例:
trait Foo[C] {
def transform(c: C)(f: C => C): C
def increment(c: C, inc: Int): C
}
接下来,在伴随对象中为您关心的类型设置 class 类型的实例:
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
}
现在我们可以使用类型class如下:
val b = A(3)
Foo[A].transform(b)(x=>x.copy(a=x.a+1)) //A(4)
Foo[A].increment(b,5) //A(8)