我想使用一些
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)
解决方法有效,但看起来很麻烦。有没有更优雅的方式来执行此行为?
从根本上说,您不能继承
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]
您可以刷新为什么案例类的继承不好以及可能的解决方法的信息:
案例类适用于函数式编程中的代数数据类型:
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
@data
, @root
/@branch
/@leaf
, @root
/@branch
/@ast
apply/unapply
,copy
等带有宏注释https://svejcar.dev/posts/2019/09/26/better-tostring-alternative-for-case-classes/
让我们想想
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
了解得太多了?