通过继承使用案例类的好处

问题描述 投票:0回答:2

我想使用一些

case class
功能,如下所示:

val base: Base = new Derived
println(base.copy())

我不能将

Base
Derived
都定义为案例类。并使其中任何一个案例类都会引发编译器错误:
Cannot resolve symbol copy
。 因此,有一个将案例类方法分派给孩子的解决方法:

abstract class BaseWorkaround(name: String) {
  def dispatchCopy: BaseWorkaround
}
case class DerivedWorkaround(name: String) extends BaseWorkaround(name) {
  override def dispatchCopy: BaseWorkaround = copy()
}

val abbie: BaseWorkaround = DerivedWorkaround("Abbie")
println(abbie.dispatchCopy)

解决方法有效,但看起来很麻烦。有没有更优雅的方式来执行此行为?

scala inheritance case-class
2个回答
1
投票

从根本上说,您不能继承

copy
因为基类的
copy
方法不知道如何复制子类的字段。

你正在做的工作,但是,如果你仔细想想,它是相当无用的:案例类是(或者,应该是设计的)不可变的,所以,如果

foo
是案例类的一个实例,那么
foo 
foo.copy()
几乎是 同一个对象 对于所有实际目的,创建这些副本而不是仅仅在任何地方使用
foo
只是纯粹的内存浪费。 (你也可以只使用
clone
而不是复制,如果这正是你出于某种原因真正想要的 - 只需让
Base
扩展
Cloneable

copy
存在的原因,如果因为它实际上需要参数
Derived.copy
的实际定义是
def copy(name: String = name)
)以方便创建与原始不同的类实例,即:
val foo = Derived("foo"); val bar = foo.copy("bar")
...

所以,如果你知道你的案例类将有哪些参数,你可以这样做:

abstract class Base[T <: Base] { 
  def name: String; 
  def copy(name: String = name): T
}

case class Derived1(name: String) extends Base[Derived1]
case class Derived2(name: String) extends Base[Derived2]

val p: Base[_] = Derived1("foo")
val pCopy = p.copy()
val s: Base[_] = Derived2("bar")
val sCopy = s.copy

请注意,虽然

Derived1
Derived2
几乎是同一件事(一个对象包装单个字符串)。虽然这在某些情况下可能仍然有用(例如,使用详细消息定义不同类型的特定于应用程序的错误),但这种方法的主要限制是您不能使用与
Base 中定义的参数集不同的参数集定义子类
:

// This won't compile:
class Derived3(name: String, code: Int) extends Base[Derived3] 

0
投票

您可以刷新为什么案例类的继承不好以及可能的解决方法的信息:

Scala 案例类继承

从另一个案例类扩展案例类

我应该在声明案例类时使用 final 修饰符吗?

案例类继承另一个类/特征

案例类继承有什么*如此*错误?

案例类之间的继承属性

案例类适用于函数式编程中的代数数据类型:

sealed trait Base
case class Derived1(i: Int) extends Base
case class Derived2(s: String) extends Base

OOP 和他们的继承很奇怪。 你应该更喜欢组合而不是继承:

case class Base(s: String)
case class Derived(i: Int, b: Base)

案例类还有其他选择:

  • 违禁品代码生成

http://eed3si9n.com/contraband-an-alternative-to-case-class/

https://www.scala-sbt.org/contraband/

https://www.scala-sbt.org/1.x/docs/Datatype.html(sbt-datatype)

https://github.com/sbt/contraband

https://www.reddit.com/r/scala/comments/5xu085/contraband_an_alternative_to_case_class/

  • 石笋
    @data

https://gitlab.com/fommil/attic/tree/master/石笋

  • 数据级
    @data

https://github.com/alexarchambault/data-class

  • Scalameta
    @data
    ,
    @root
    /
    @branch
    /
    @leaf
    ,
    @root
    /
    @branch
    /
    @ast

https://github.com/scalameta/scalameta/blob/v4.7.5/scalameta/common/shared/src/main/scala/org/scalameta/data/data.scala#L9-L21

https://github.com/scalameta/scalameta/blob/v4.7.5/scalameta/common/shared/src/main/scala/org/scalameta/adt/Adt.scala#L11-L19

https://github.com/scalameta/scalameta/blob/v4.7.5/scalameta/common/shared/src/main/scala/scala/meta/internal/trees/root.scala#L11

https://github.com/scalameta/scalameta/blob/v4.7.5/scalameta/common/shared/src/main/scala/scala/meta/internal/trees/branch.scala#L11

https://github.com/scalameta/scalameta/blob/v4.7.5/scalameta/common/shared/src/main/scala/scala/meta/internal/trees/ast.scala#L13

  • 生成方法
    apply/unapply
    copy
    等带有宏注释

生成创建类的应用方法

如何高效/干净地覆盖复制方法

禁止生成申请案例类

  • 类型类的泛型推导

https://svejcar.dev/posts/2019/09/26/better-tostring-alternative-for-case-classes/

https://github.com/milesssabin/shapeless/blob/main/examples/src/main/scala/shapeless/examples/alacarte.scala

https://github.com/milesssabin/shapeless/blob/main/core/shared/src/test/scala/shapeless/alacarte.scala


让我们想想

copy
中的签名
Base
应该有什么。我的理解是你的意思是

sealed trait Base {
  type This <: Base
  def name: String
  def age: Int
  def copy(name: String = name, age: Int = age): This
}
case class Derived(name: String, age: Int) extends Base {
  type This = Derived
  def copy(name: String = name, age: Int = age): Derived = Derived(name, age)
}

sealed trait Base {
  def name: String
  def age: Int
  def copy(name: String = name, age: Int = age): Base
}
case class Derived(name: String, age: Int) extends Base {
  def copy(name: String = name, age: Int = age): Derived = Derived(name, age)
}

原则上,

def copy
中的
Derived
def copy
中的
Base
,Base中的
def name
,Base中的
def age
都可以由
case class Derived(name: String, age: Int)
生成。但是如果
Base
有几个继承者呢?
Base
现在是不是对
Derived
了解得太多了?

© www.soinside.com 2019 - 2024. All rights reserved.