Поиск…


Синтаксис

  • черта 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)
  }
}

Линеаризация происходит от начала до фронта . В этом случае,

  1. Первая Shape будет линеаризована, которая выглядит так:

    Shape -> AnyRef -> Any

  2. Затем Dotted линеаризуется:

    Dotted -> Border -> Shape -> AnyRef -> Any

  3. Следующий в строке - Blue . Обычно линеаризация Blue :

    Blue -> Color -> Shape -> AnyRef -> Any

    потому что в линеаризации MyShape до сих пор ( Шаг 2 ) уже появился Shape -> AnyRef -> Any . Следовательно, он игнорируется. Таким образом, Blue линеаризации будет:

    Blue -> Color -> Dotted -> Border -> Shape -> AnyRef -> Any

  4. Наконец, будет добавлен Circle и окончательный порядок линеаризации будет:

    Круг -> Синий -> Цвет -> Пунктирный -> Граница -> Форма -> AnyRef -> Любой

Этот порядок линеаризации решает порядок обращения методов, когда super используется в любом классе или признаке. Первая реализация метода справа вызывается в порядке линеаризации. Если new MyShape().paint("Circle ") , он будет печатать:

Circle with Blue Color with Dotted Border 

Более подробную информацию о линеаризации можно найти здесь .



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow