Scala:在父方法中维护子类?

时间:2018-12-24 19:03:10

标签: scala functional-programming

有父母时:

abstract class Parent {
   def something(arg: ???): Parent = ???
}

class Child extends Parent {}

我想要

val updatedChild = new Child().something(...)

updatedChild的类型为Child,而不是类型为Parent,这可能吗?

4 个答案:

答案 0 :(得分:2)

一种实现方法是参数化父对象:

 abstract class Parent[T <: Parent[T]] {
    def something(arg: Foo): T
 }

 class Child(val foo: String) extends Parent[Child] {
    def something(arg: String) = return new Child(arg)
 }

有时,您还可以使用this.type

class Parent {
  def something(arg: Foo): this.type = this
}
class Child {
   override def something(arg: Foo) = this
}

但是后一种方法仅在您要返回的所有内容是thisthis.type不是ParentChild而是只有一个实例-this)。

答案 1 :(得分:1)

看来您可以做到:

class Parent[THIS <: Parent[THIS]] {
   def something: THIS
}

这似乎可行。

我不确定这是否是您应该做的事情。

答案 2 :(得分:1)

以下是实际上是编译的提案

abstract class Parent[Repr <: Parent[Repr]] {
  def something(arg: Int): Repr
}

这是您可以做到的,至少没有明确建议不要这样做。标准收藏库经常使用它,例如IterableLike作为这种F界多态性的典型示例。

答案 3 :(得分:0)

安德烈(Andrey)和迪玛(Dima)的答案都涵盖了仅使用oo模式解决问题的一种方法。

但是我想指出另一种称为 typeclasses (在函数式语言中更常见)的方法,如果您打算编写通用函数,这将很有帮助使用您的界面。

首先,有一个接口而不是父类,该接口描述了可以对类型类的实例执行的操作。

trait Typeclass[T] {
  def something(t: T)(arg: Foo): T
}

然后,您将定义类型,这一次它们不扩展任何父类,因此不必重写任何内容。

class Child {
  ...
}

现在,您必须证明您的类型是类型类的实例。
(一个常见的实现方式是在类的伴随对象中)

object Child {
  implicit final val ChildTypeclass: Typeclass[Child] = new Typeclass[Child] {
    override def something(child: Child)(arg: Foo): Child = ???
  }
}

最后,定义一个通用方法,该方法可以在任何类型T上操作,只要该类型的实例是该类型的实例即可。

def generic[T](t: T, arg: Foo)(implicit tt: Typeclass[T]): T =
  tt.something(t)(arg)

奖金,如果您想恢复“点符号” ,则可以向Typeclass添加Ops模式。

object syntax {
  object typeclass {
     implicit final class TypeclassOps[T](val t: T) extends AnyVal {
       final def something(arg: Foo)(implicit tt: Typelcass[T]) =
         tt.something(t)(arg)
     }
  }
}

import syntax.typeclasss._

def generic[T: Typelcass](t: T, arg: Foo): T
  t.something(arg)

val newChild = generic(new Child, new Foo)
// newChild: Child = ???

此外,一种常见的方法是在您的类中定义something方法,并且typeclass实例将调用转发给该类中定义的方法,这样您就可以在{{1 }},而不必放置所有类型类的机器。

我必须说,这对于非常高级的抽象很有用,您计划为这些抽象提供许多类型的实例((甚至是您控制范围之外的类型,如任何标准集合类型),并且编写非常可以在任何这些函数上运行的通用函数。
如果不是这样,则F边界类型似乎是更合理的解决方案。