18

我知道你不允许从案例类继承,但是当你真的需要时你会怎么做?我们在一个层次结构中有两个类,它们都包含许多字段,我们需要能够创建两者的实例。这是我的选择:

  • 如果我将超类设为普通类而不是案例类 - 我将失去所有案例类的优点,例如 toString、equals、hashCode 方法等。
  • 如果我把它作为一个案例类,我会打破不从案例类继承的规则。
  • 如果我在子类中使用组合 - 我必须编写很多方法并将它们重定向到另一个类 - 这将意味着大量工作并且会感觉非 Scalaish。

我该怎么办?这不是很普遍的问题吗?

4

3 回答 3

14

是的,这是一个经常出现的问题,我建议创建一个具有所有父属性的特征,创建一个仅实现它的案例类,然后创建另一个继承它的具有更多属性的案例类。

sealed trait Parent {
  /* implement all common properties */
}

case class A extends Parent

case class B extends Parent {
  /*add all stuff you want*/
}

看它的一个好方法是一棵树,特征是节点,案例类是叶子。

您可以根据对父级的需要使用特征或抽象类。但是,请避免使用类,因为您将能够创建它的实例,这并不优雅。

编辑:正如评论中所建议的,如果不是所有案例类都包含在模式匹配中,您可以密封特征以便在编译时出现异常。例如,在“Scala 编程”的第 15.5 章中对此进行了说明

于 2012-05-18T07:30:28.033 回答
6

用委托代替继承怎么样?

如果层次结构中的两个类有很多共享字段,那么委托可以减少样板代码的数量吗?像这样:

case class Something(aa: A, bb: B, cc: C, payload: Payload)

sealed abstract class Payload

case class PayloadX(xx: X) extends Payload
case class PayloadY(yy: Y) extends Payload

然后你会Something像这样创建实例:

val sth1 = Something('aa', 'bb', 'cc', PayloadX('xx'))
val sth2 = Something('aa', 'bb', 'cc', PayloadY('yy'))

你可以做模式匹配:

sth1 match {
  case Something(_, _, _, PayloadX(_)) => ...
  case Something(_, _, _, PayloadY(_)) => ...
}

好处: (?)

  • 当您声明PayloadXPayloadY时,您不必重复Something中的所有字段。

  • 当您创建 的实例时Something(... Payload(..)),您可以重用创建的代码Something,无论是在创建Something(... PayloadX(..))和时...PayloadY

缺点: (?)

  • 也许PayloadXY在你的情况下实际上是真正的子类Something,我的意思是,在你的情况下,也许委托在语义上是错误的?

  • 你将不得不写something.payload.whatever而不是简单地写something.whatever(我想这可能是好是坏取决于你的具体情况?)

于 2012-08-16T05:44:55.440 回答
4

我也探讨了这个问题,AFAIK,你会得到的最好的是:

让每个案例类从定义每个案例类必须实现的抽象属性的公共特征扩展

它不会删除样板(根本),但定义了您的案例类必须遵守的合同,同时不会丢失案例类功能集......

于 2012-05-18T07:30:03.720 回答