我知道在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)
到目前为止,这么好。但是,我真正想要的是能够在Foo中编写一个方法foo()
,当在类型为Bar的对象上调用时返回一个Bar类型的对象,而不必重新实现类Bar中的方法。有没有办法在Scala中执行此操作?
答案 0 :(得分:5)
是的,可以做到。一个很好的例子是集合库。
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)
请参阅The Architecture of Scala Collections了解其完成情况。
修改强>: 它使用两种方法来重用实现。第一种是使用常见的特征和构建器,另一种是使用类型类。
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!)
这样做的全部意义在于,您可以在共同特征上做一些有趣的事情,例如在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
答案 1 :(得分:4)
复制方法由编译器实现,它似乎不属于常见特征。最简单的方法是定义一个特征:
trait HasFoo[T] {
def foo(g:String): T
}
case class Foo( f: String ) extends HasFoo[Foo] {
def foo( g: String ) = copy(f=g)
}
case class Bar( f: String ) extends HasFoo[Bar] {
def foo( g: String ) = copy(f=g)
}
scala> Bar("a").foo("b")
res7: Bar = Bar(b)
scala> Foo("a").foo("b")
res8: Foo = Foo(b)
另一种选择是使用类型类来提供适当的构建器。但它不会保存打字字符的数量。
答案 2 :(得分:3)
注意:这不会创建新对象,但会重新使用this
对象。一般用途,请参阅paradigmatic’s answer。
出于某种原因,它不能与case class
的{{1}}方法一起使用。 (但诚然,由于copy
继承不应该进行,因此问题不会发生。)但对于任何其他方法,您都可以使用case class
。
this.type
如果您需要方法参数和方法体的自我类型方差(而不是仅返回类型方差),则需要更进一步并定义
case class Foo(val f: String) { def foo(g: String): this.type = { this }}
case class Bar(override val f: String) extends Foo(f)
Bar("F").foo("G")
res: Bar = Foo(F)
这将允许您向trait HasFoo[T <: HasFoo[T]] { this: T =>
def foo(g:String): T
def bar(g: T): T // here may follow an implementation
}
添加适当的方法体。 (见:proper class hierarchy for 2D and 3D vectors)
答案 3 :(得分:-3)
此解决方案不需要单独的特征。
class Bar
class Foo {
def returnMyType[A](x:A) :A = { println(x); x }
}
val f = new Foo
val b = new Bar
val bReturned = f.returnMyType(b)
println(bReturned.getClass.getName)