Должен ли я использовать последний модификатор при объявлении классов case?


Согласно инструменту статического анализа scala-wartremover я должен поставить "final" перед каждым классом case, который я создаю: сообщение об ошибке гласит: "классы case должны быть final".

Согласно scapegoat (другой инструмент статического анализа для Scala) вместо этого я не должен (сообщение об ошибке: "избыточный конечный модификатор на классе case")

Кто прав и почему?

1 38

1 ответ:

Он не является избыточным в том смысле, что его использование действительно меняет вещи. Как и следовало ожидать, вы не можете расширить конечный класс case, но вы можете расширить не окончательный. Почему wartremover предполагает, что классы case должны быть окончательными? Ну, потому что расширять их-не очень хорошая идея. Рассмотрим это:

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? Может ли это быть правдой?

Конечно, мы можно исправить это, переопределив .equals.hashCode, и .toString, и .unapply, и .copy, а также, возможно,, .productIterator, .productArity, .productElement и т.д.) метод на Bar и Baz. Но "из коробки", любой класс, который расширяет класс case будет сломан.

Это причина, вы больше не можете расширить класс case другим классом case, это было запрещено с тех пор, я думаю, scala 2.11. Расширение класса case на класс, не являющийся case, по-прежнему допускается, но, по крайней мере, по мнению wartremover, это не совсем так. лучшая идея.