我开始使用 Scala。如果我希望将其参数公开为属性,我是否应该将一个类定义为案例类?它不会引入任何副作用吗?
4 回答
为案例类生成的样板代码在字节码中具有很小但非零的成本。除了copy
方法之外,还有hashCode
,equals
以及toString
伴随对象工厂方法。
更重要的是,从案例类派生类是不可取的。从案例类派生案例类确实会引起问题(编译器会对您大喊大叫)。特别是,copy(...)
编译器不会生成覆盖方法,因此如果您尝试复制从案例类派生的案例类,您可能会遇到一些奇怪的失败模式。
如果你把你的案例类放在任何继承图的叶子上,你会没事的。
您将获得定义的任何参数的属性,它们将是 vals(即决赛)
case class User(name: String, group: String)
val user = User("jsmith", "admins")
// access both properties
println("name: %s group: %s".format(user.name, user.group))
您也可以通过常规(即非案例类)获得此行为:
// creates two final public properties as well
class User(val name: String, val group: String)
// creates read/write public properties
class User(var name: String, var group: String)
val user = new User("jsmith", "admins")
user.group = "guests"
Case 类还带来了许多其他的东西,例如相等、hashcode 和 toString 的有用实现,以及一个带有工厂方法的伴随对象,它消除了使用 new 的需要。
如您所见,不需要案例类来实现您想要的,但它可以让您快速实现目标。至于副作用,定义一个案例类会在幕后生成一些代码,以提供上一段中描述的内容。这些通常很有用,我倾向于不担心它们,但了解它们是件好事。
除了已经提到的几点之外,案例类在概念上与您在 Java 中所谓的“值类”很接近。当然,没有什么能阻止您编写可变案例类或功能繁重但数据很少的案例类,但这可能会让使用您的代码的其他人感到惊讶。根据经验,我会说在创建案例类之前至少三思而后行……
- 需要可变状态
- 当您不确定以后是否需要子类时
- 如果它们的主要目的是计算事物(而不是代表事物),并且这些计算很复杂
- 如果您需要对其行为进行细粒度控制(例如,您需要自定义模式匹配)
- 当性能真的很重要时
- 当您只需要“案例类功能”之一时,例如我会考虑使用案例类只是为了避免在构造函数参数前面键入“val”作为矫枉过正
其他答案都很好,但他们错过的一个真正风险是案例类具有值相等性,其行为与标准的面向对象的身份相等性非常不同。如果所有字段都相等,则两个 case 对象相等。这正是在某些情况下所需要的,但在其他情况下却非常出乎意料。特别是,将可变状态添加到值相等的东西是自找麻烦。你可以很容易地发现哈希码随时间变化的对象,从而导致 HashMap 损坏和其他类似的问题。
将某些东西声明为案例类以节省一些将您的属性声明为“val”的击键是错误的经济,并且有一天会咬你。