我知道在Scala中不推荐使用case类继承,但为了简单起见,我在下面的例子中使用了它:
scala> case class Foo(val f: String) { def foo(g: String): Foo = { this.copy(f=g) }}
defined class Foo
scala> case class Bar(override val f: String) extends Foo(f)
warning: there were 1 deprecation warnings; re-run with -deprecation for details
defined class Bar
scala> Bar("F")
res0: Bar = Foo(F)
scala> res0.foo("G")
res1: Foo = Foo(G)
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.但是,我真正想要的是能够foo()在Foo中编写一个方法,当在类型为Bar的对象上调用时返回Bar类型的对象,而不必重新实现类Bar中的方法.有没有办法在Scala中执行此操作?
是的,可以做到.一个很好的例子是集合库.
scala> List(1, 2, 3) take 2
res1: List[Int] = List(1, 2)
scala> Array(1, 2, 3) take 2
res2: Array[Int] = Array(1, 2)
Run Code Online (Sandbox Code Playgroud)
请参阅Scala集合的体系结构以了解它是如何完成的.
编辑:它使用两种方法来重用实现.第一种是使用常见的特征和构建器,另一种是使用类型类.
scala> :paste
// Entering paste mode (ctrl-D to finish)
trait Builder[A] {
def apply(f: String): A
}
trait FooLike[A] {
def builder: Builder[A]
def f: String
def genericCopy(f: String): A = builder(f)
def map(fun: String => String): A = builder(fun(f))
}
case class Foo(f: String) extends FooLike[Foo] {
def builder = new Builder[Foo] {
def apply(f: String): Foo = Foo(f)
}
}
case class Bar(f: String) extends FooLike[Bar] {
def builder = new Builder[Bar] {
def apply(f: String): Bar = Bar(f)
}
}
scala> Foo("foo").genericCopy("something")
res0: Foo = Foo(something)
scala> Bar("bar").genericCopy("something")
res1: Bar = Bar(something)
scala> Foo("foo") map { _ + "!" }
res2: Foo = Foo(foo!)
Run Code Online (Sandbox Code Playgroud)
这样做的整点,那么你可以做一些事情的共同特点有趣,就像实现共同map在FooLike.用琐碎的代码很难看到它带来的好处.
使用类型类的好处是,您可以添加功能Foo,Bar甚至在您无法更改功能时(例如String).
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Foo(f: String)
case class Bar(f: String)
trait CanCopy[A] {
def apply(self: A, f: String): A
def f(self: A): String
}
object CanCopy {
implicit val fooCanCopy = new CanCopy[Foo] {
def apply(v: Foo, f: String): Foo = v.copy(f = f)
def f(v: Foo) = v.f
}
implicit val barCanCopy = new CanCopy[Bar] {
def apply(v: Bar, f: String): Bar = v.copy(f = f)
def f(v: Bar) = v.f
}
implicit val stringCanCopy = new CanCopy[String] {
def apply(v: String, f: String): String = f
def f(v: String) = v
}
def copy[A : CanCopy](v: A, f: String) = {
val can = implicitly[CanCopy[A]]
can(v, f)
}
def f[A : CanCopy](v: A) = implicitly[CanCopy[A]].f(v)
}
scala> CanCopy.copy(Foo("foo"), "something")
res1: Foo = Foo(something)
scala> CanCopy.f(Foo("foo"))
res2: String = foo
scala> CanCopy.copy(Bar("bar"), "something")
res3: Bar = Bar(something)
scala> CanCopy.copy("string", "something")
res4: java.lang.String = something
Run Code Online (Sandbox Code Playgroud)
注意:这不会创建新对象,而是重新使用该this对象。对于一般用途,请参阅范例\xe2\x80\x99s 答案。
由于某种原因,它不能与case class\xe2\x80\x99scopy方法一起使用。(但不可否认,由于case class无论如何都不应该进行继承,因此不会出现该问题。)。但对于任何其他方法,您可以使用this.type.
case class Foo(val f: String) { def foo(g: String): this.type = { this }}\n\ncase class Bar(override val f: String) extends Foo(f)\n\nBar("F").foo("G")\nres: Bar = Foo(F)\nRun Code Online (Sandbox Code Playgroud)\n\n如果您需要方法参数和方法主体中的自类型差异(而不是仅返回类型差异),则需要更进一步并定义
\n\ntrait HasFoo[T <: HasFoo[T]] { this: T =>\n def foo(g:String): T\n def bar(g: T): T // here may follow an implementation\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这将允许您向trait. (参见:2D 和 3D 向量的正确类层次结构)