Scala Language
Черты
Поиск…
Синтаксис
- черта ATrait {...}
- класс AClass (...) расширяет ATrait {...}
- класс AClass расширяет BClass с помощью ATrait
- класс AClass расширяет ATrait с помощью BTrait
- класс AClass расширяет ATrait с помощью BTrait с помощью CTrait
- класс ATrait расширяет BTrait
Сложная модификация с чертами
Вы можете использовать черты для изменения методов класса, используя черты в стиле стекирования.
В следующем примере показано, как можно складывать черты. Важное значение имеет упорядочение признаков. Используя различный порядок признаков, достигается различное поведение.
class Ball {
def roll(ball : String) = println("Rolling : " + ball)
}
trait Red extends Ball {
override def roll(ball : String) = super.roll("Red-" + ball)
}
trait Green extends Ball {
override def roll(ball : String) = super.roll("Green-" + ball)
}
trait Shiny extends Ball {
override def roll(ball : String) = super.roll("Shiny-" + ball)
}
object Balls {
def main(args: Array[String]) {
val ball1 = new Ball with Shiny with Red
ball1.roll("Ball-1") // Rolling : Shiny-Red-Ball-1
val ball2 = new Ball with Green with Shiny
ball2.roll("Ball-2") // Rolling : Green-Shiny-Ball-2
}
}
Обратите внимание, что super
используется для вызова метода roll()
в обоих признаках. Только таким образом мы можем достичь штабелируемой модификации. В случаях суммируемой модификации порядок вызова метода определяется правилом линеаризации .
Основные черты
Это самая базовая версия признака в Scala.
trait Identifiable {
def getIdentifier: String
def printIndentification(): Unit = println(getIdentifier)
}
case class Puppy(id: String, name: String) extends Identifiable {
def getIdentifier: String = s"$name has id $id"
}
Поскольку ни супер класс не объявлен для признака Identifiable
, по умолчанию она простирается от AnyRef
класса. Поскольку определение getIdentifier
не предусмотрено в Identifiable
, класс Puppy
должен его реализовать. Однако Puppy
наследует реализацию printIdentification
от Identifiable
.
В REPL:
val p = new Puppy("K9", "Rex")
p.getIdentifier // res0: String = Rex has id K9
p.printIndentification() // Rex has id K9
Решение проблемы алмаза
Проблема с алмазом или множественное наследование обрабатывается Scala с использованием Traits, которые аналогичны интерфейсам Java. Черты более гибкие, чем интерфейсы, и могут включать реализованные методы. Это делает черты похожими на mixins на других языках.
Scala не поддерживает наследование от нескольких классов, но пользователь может расширять несколько признаков в одном классе:
trait traitA {
def name = println("This is the 'grandparent' trait.")
}
trait traitB extends traitA {
override def name = {
println("B is a child of A.")
super.name
}
}
trait traitC extends traitA {
override def name = {
println("C is a child of A.")
super.name
}
}
object grandChild extends traitB with traitC
grandChild.name
Здесь grandChild
наследует как traitB
и traitC
, которые, в свою очередь, наследуют от traitA
. На выходе (ниже) также показан порядок приоритета при первом разрешении реализации метода:
C is a child of A.
B is a child of A.
This is the 'grandparent' trait.
Обратите внимание, что когда super
используется для вызова методов в class
или trait
, правило линеаризации входит в игру для решения иерархии вызовов. Порядок линеаризации для grandChild
будет:
grandChild -> traitC -> traitB -> traitA -> AnyRef -> Any
Ниже приведен еще один пример:
trait Printer {
def print(msg : String) = println (msg)
}
trait DelimitWithHyphen extends Printer {
override def print(msg : String) {
println("-------------")
super.print(msg)
}
}
trait DelimitWithStar extends Printer {
override def print(msg : String) {
println("*************")
super.print(msg)
}
}
class CustomPrinter extends Printer with DelimitWithHyphen with DelimitWithStar
object TestPrinter{
def main(args: Array[String]) {
new CustomPrinter().print("Hello World!")
}
}
Эта программа печатает:
*************
-------------
Hello World!
Линеаризация для CustomPrinter
будет:
CustomPrinter -> DelimitWithStar -> DelimitWithHyphen -> Принтер -> AnyRef -> Любой
Линеаризация
В случае стекируемой модификации Scala устраивает классы и черты в линейном порядке для определения иерархии вызовов метода, которая известна как линеаризация . Правило линеаризации используется только для тех методов, которые включают вызов метода через super()
. Рассмотрим это на примере:
class Shape {
def paint (shape: String): Unit = {
println(shape)
}
}
trait Color extends Shape {
abstract override def paint (shape : String) {
super.paint(shape + "Color ")
}
}
trait Blue extends Color {
abstract override def paint (shape : String) {
super.paint(shape + "with Blue ")
}
}
trait Border extends Shape {
abstract override def paint (shape : String) {
super.paint(shape + "Border ")
}
}
trait Dotted extends Border {
abstract override def paint (shape : String) {
super.paint(shape + "with Dotted ")
}
}
class MyShape extends Shape with Dotted with Blue {
override def paint (shape : String) {
super.paint(shape)
}
}
Линеаризация происходит от начала до фронта . В этом случае,
Первая
Shape
будет линеаризована, которая выглядит так:Shape -> AnyRef -> Any
Затем
Dotted
линеаризуется:Dotted -> Border -> Shape -> AnyRef -> Any
Следующий в строке -
Blue
. Обычно линеаризацияBlue
:Blue -> Color -> Shape -> AnyRef -> Any
потому что в линеаризации
MyShape
до сих пор ( Шаг 2 ) уже появилсяShape -> AnyRef -> Any
. Следовательно, он игнорируется. Таким образом,Blue
линеаризации будет:Blue -> Color -> Dotted -> Border -> Shape -> AnyRef -> Any
Наконец, будет добавлен
Circle
и окончательный порядок линеаризации будет:Круг -> Синий -> Цвет -> Пунктирный -> Граница -> Форма -> AnyRef -> Любой
Этот порядок линеаризации решает порядок обращения методов, когда super
используется в любом классе или признаке. Первая реализация метода справа вызывается в порядке линеаризации. Если new MyShape().paint("Circle ")
, он будет печатать:
Circle with Blue Color with Dotted Border
Более подробную информацию о линеаризации можно найти здесь .