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

62
根据 Scala 静态分析工具 scala-wartremover,我需要在创建的每个 case 类前面加上 "final":错误消息指出“case 类必须是 final 的”。
而根据另一个 Scala 静态分析工具 scapegoat 的建议,我不应该这样做(错误消息:“case 类上的冗余 final 修饰符”)。
谁是正确的,为什么?
1个回答

113

在使用它时,它并不是多余的,因为使用它确实会改变事物。正如人们所预期的,您不能扩展一个终态(final)的case class,但可以扩展一个非终态的case class。

那么,为什么wartremover建议将case class设置为final呢?这是因为将它们扩展并不是一个好主意。考虑以下内容:

scala> case class Foo(v:Int)
defined class Foo

scala> class Bar(v: Int, val x: Int) extends Foo(v)
defined class Bar

scala> new Bar(1, 1) == new Bar(1, 1)
res25: Boolean = true

scala> new Bar(1, 1) == new Bar(1, 2)
res26: Boolean = true
// ????

真的吗?Bar(1,1)等于Bar(1,2)?这不是预料中的。但是,稍等,还有更多:

scala> new Bar(1,1) == Foo(1)
res27: Boolean = true

scala> class Baz(v: Int) extends Foo(v)
defined class Baz

scala> new Baz(1) == new Bar(1,1)
res29: Boolean = true //???

scala> println (new Bar(1,1))
Foo(1) // ???

scala> new Bar(1,2).copy()
res49: Foo = Foo(1) // ???

Bar的复制品具有类型Foo?这可能是正确的吗?

当然,我们可以通过覆盖BarBaz上的.equals(以及.hashCode.toString.unapply.copy、可能还有.productIterator.productArity.productElement等)方法来修复这个问题。但是,“开箱即用”,任何扩展案例类的类都将出现问题。

这就是为什么自Scala 2.11以来,不能再通过另一个案例类来扩展案例类的原因。仍然允许通过非案例类来扩展案例类,但至少在wartremover的观点中,这并不是一个好主意。


8
哇,那听起来确实很混乱!为什么他们没有禁止案例类继承类呢?因为这也会带来非常类似的问题。 - sscarduzio
5
这是由于源代码兼容性的原因。正如(https://twitter.com/viktorklang/status/735556776989491201)所解释的那样,这样的修改将意味着主版本号的增加,而目前(或至少目前)Lightbend似乎没有计划进行这样的更改。 - fanf42
@Dima,我有Scala 2.12,但它仍然允许扩展case class。 - tintin
4
可以(仍然)扩展一个case class,但是不能使用另一个case class。 - Dima

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接