如何在需要层次结构时使用案例类?

uzi*_*lan 15 scala case-class

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

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

我该怎么办?这不是一个常见的问题吗?

Chr*_*che 10

是的,这是一个经常出现的问题,我建议创建一个包含所有父属性的特征,创建一个只实现它的case类,然后再创建另一个具有更多属性的case类.

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

case class A extends Parent

case class B extends Parent {
  /*add all stuff you want*/
}
Run Code Online (Sandbox Code Playgroud)

看到它的好方法是树,特征是节点,案例类是叶子.

您可以根据父母的需要使用特征或抽象类.但是,避免使用类,因为您可以创建它的实例,这不是很优雅.

编辑:正如评论中所建议的那样,如果并非所有案例类都包含在模式匹配中,则可以密封特征以便在编译时具有异常.例如,在"Scala编程"的第15.5章中对此进行了解释.

  • 密封特性也是一个好主意. (6认同)

Kaj*_*nus 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
Run Code Online (Sandbox Code Playgroud)

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

val sth1 = Something('aa', 'bb', 'cc', PayloadX('xx'))
val sth2 = Something('aa', 'bb', 'cc', PayloadY('yy'))
Run Code Online (Sandbox Code Playgroud)

你可以做模式匹配:

sth1 match {
  case Something(_, _, _, PayloadX(_)) => ...
  case Something(_, _, _, PayloadY(_)) => ...
}
Run Code Online (Sandbox Code Playgroud)

好处:(?)

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

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

缺点: (?)

  • 也许PayloadXY你的情况实际上是真正的子类Something,我的意思是,你的情况,也许代表团语义错了吗?

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