Абстрактные классы

Когда необходимо написать класс, но известно, что в нем будут абстрактные члены, можно создать либо trait, либо абстрактный класс. В большинстве случаев желательно использовать trait, но исторически сложилось так, что было две ситуации, когда предпочтительнее использование абстрактного класса:

Базовый класс, который принимает аргументы конструктора

До Scala 3, когда базовому классу нужно было принимать аргументы конструктора, он объявлялся как абстрактный класс:

abstract class Pet(name: String):
  def greeting: String
  def age: Int
  override def toString = s"My name is $name, I say $greeting, and I’m $age"

class Dog(name: String, age: Int) extends Pet(name):
  val greeting = "Woof"

val d = Dog("Fido", 1)

Однако в Scala 3 trait-ы могут иметь параметры, так что теперь в той же ситуации можно использовать trait-ы:

trait Pet(name: String):
  def greeting: String
  def age: Int
  override def toString = s"My name is $name, I say $greeting, and I’m $age"

class Dog(name: String, var age: Int) extends Pet(name):
  val greeting = "Woof"

val d = Dog("Fido", 1)

trait-ы более гибки в составлении, потому что можно смешивать (наследовать) несколько trait-ов, но только один класс. В большинстве случаев trait-ы следует предпочитать классам и абстрактным классам. Правило выбора состоит в том, чтобы использовать классы всякий раз, когда необходимо создавать экземпляры определенного типа, и trait-ы, когда желательно разложить и повторно использовать поведение.


Ссылки: