Смешивание нескольких признаков в Scala

Быстрое примечание: примеры из учебника Scala для Java Refugees Часть 5: Черты и типы.

Предположим, что у меня есть черты Student, Worker, Underpaid и Young.

Как я могу объявить класс (не экземпляр), CollegeStudent, со всеми этими признаками?

Примечание. Я знаю о случаях простых дел, таких как CollegeStudent с одной или двумя чертами:

class CollegeStudent extends Student with Worker

Ответ 1

Легко, когда вы объявляете класс, вы просто используете ключевое слово "с" так часто, как хотите

class CollegeStudent extends Student with Worker with Underpaid with Young

порядок признаков может быть важен, если свойство изменяет поведение класса, все зависит от используемых вами черт.

Также, если вы не хотите иметь класс, который всегда использует одни и те же черты, вы можете использовать их позже:

class CollegeStudent extends Student
new CollegeStudent with Worker with Underpaid with NotSoYoungAnymore

Ответ 2

Я думаю, что очень важно объяснить не только синтаксис, но и , какую роль играет упорядочение характеристик. Я нашел объяснение в Jason Swartz Изучение Scala (стр. 177) довольно информативно.

  • Класс Scala может распространять сразу несколько признаков, но классы JVM могут распространять только один родительский класс. Компилятор Scala решает это, создавая "копии каждого признака, чтобы сформировать высокую иерархию с одним столбцом класс и черты ", процесс, известный как линеаризация.

  • В этом контексте расширение нескольких признаков с идентичными именами полей не будет скомпилировано, точно так же, "как если бы вы расширяли класс и предоставляли свою собственную версию метода, но не смогли добавить ключевое слово переопределения".

И поскольку он определяет форму дерева наследования, порядок линеаризации действительно является очень важным вопросом. В качестве примера, class D extends A with B with C (где A - класс и B и C - признаки) станет class D extends C extends B extends A. Следующие несколько строк, также из книги, прекрасно иллюстрируют:

trait Base { override def toString = "Base" }
class A extends Base { override def toString = "A->" + super.toString }
trait B extends Base { override def toString = "B->" + super.toString }
trait C extends Base { override def toString = "C->" + super.toString }
class D extends A with B with C { override def toString = "D->" + super.toString }

При вызове new D() REPL напечатает следующее:

 D->C->B->A->Base

Что отлично отражает структуру линеаризованного графика наследования.