Смешивание нескольких черт в Scala
Краткое примечание: примеры из учебника Scala для Java-беженцев. Часть 5. Особенности и типы.
Предположим, у меня есть черты: студент, работник, недоплата и молодой.
Как я могу объявить класс (не экземпляр) CollegeStudent со всеми этими чертами?
Примечание. Мне известны случаи простейших, например CollegeStudent с одной или двумя чертами:
class CollegeStudent extends Student with Worker
2 ответа
Это легко, когда объявляете класс, вы просто используете ключевое слово "с" так часто, как хотите
class CollegeStudent extends Student with Worker with Underpaid with Young
Порядок черт может быть важен, если черта меняет поведение класса, все зависит от черт, которые вы используете.
Также, если вы не хотите иметь класс, который всегда использует одни и те же черты, вы можете использовать их позже:
class CollegeStudent extends Student
new CollegeStudent with Worker with Underpaid with NotSoYoungAnymore
Я думаю, что очень важно объяснить не только синтаксис, но и то, какую роль играет упорядочение признаков. Я нашел объяснение в " Обучающей скале" Джейсона Шварца (стр. 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
Что отлично отражает структуру линеаризованного графа наследования.