Szukaj…


Składnia

  • cecha ATrait {...}
  • klasa AClass (...) rozszerza ATrait {...}
  • klasa AClass rozszerza klasę BClass o ATrait
  • klasa AClass rozszerza ATrait o BTrait
  • klasa AClass rozszerza ATrait o BTrait o CTrait
  • klasa ATrait rozszerza BTrait

Modyfikacja stosów z cechami

Możesz użyć cech, aby zmodyfikować metody klasy, używając cech w sposób umożliwiający ustawianie jeden na drugim.

Poniższy przykład pokazuje, w jaki sposób można zestawiać cechy. Kolejność cech jest ważna. Stosując inną kolejność cech, osiąga się różne zachowanie.

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
  }
}

Zauważ, że super służy do wywoływania metody roll() w obu cechach. Tylko w ten sposób możemy osiągnąć modyfikację stosową. W przypadku modyfikacji stosu kolejność wywoływania metod jest określana przez regułę linearyzacji .

Podstawy cechy

Jest to najbardziej podstawowa wersja cechy w Scali.

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"
}

Ponieważ żadna AnyRef nie jest zadeklarowana dla cechy Identifiable , domyślnie rozciąga się AnyRef klasę AnyRef . Ponieważ w Identifiable podano definicji getIdentifier , klasa Puppy musi ją zaimplementować. Jednak Puppy dziedziczy implementację printIdentification po Identifiable .

W REPL:

val p = new Puppy("K9", "Rex")
p.getIdentifier  // res0: String = Rex has id K9
p.printIndentification()  // Rex has id K9

Rozwiązywanie problemu diamentów

Problem z diamentem lub wielokrotne dziedziczenie jest rozwiązywany przez Scalę za pomocą Cech, które są podobne do interfejsów Java. Cechy są bardziej elastyczne niż interfejsy i mogą obejmować zaimplementowane metody. To sprawia, że cechy są podobne do mixin w innych językach.

Scala nie obsługuje dziedziczenia z wielu klas, ale użytkownik może rozszerzyć wiele cech w jednej klasie:

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

Tutaj grandChild dziedziczy po obu traitB i traitC , które z kolei dziedziczą po traitA Dane wyjściowe (poniżej) pokazują również kolejność pierwszeństwa przy ustalaniu, które implementacje metod są wywoływane jako pierwsze:

C is a child of A. 
B is a child of A. 
This is the 'grandparent' trait.

Zauważ, że gdy super jest używane do wywoływania metod w class lub trait , w grę wchodzi reguła linearyzacji , która decyduje o hierarchii połączeń. Kolejność linearyzacji dla grandChild będzie:

grandChild -> traitC -> traitB -> traitA -> AnyRef -> Any


Poniżej znajduje się inny przykład:

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!")
  }
}

Ten program drukuje:

*************
-------------
Hello World!

Linearyzacja dla CustomPrinter będzie:

CustomPrinter -> DelimitWithStar -> DelimitWithHyphen -> Drukarka -> AnyRef -> Dowolny

Linearyzacja

W przypadku modyfikacji , którą można ustawiać jeden na drugim , Scala układa klasy i cechy w kolejności liniowej, aby określić hierarchię wywołań metod, zwaną linearyzacją . Reguła linearyzacji jest używana tylko dla tych metod, które wymagają wywołania metody przez super() . Rozważmy to na przykładzie:

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)
  }
}

Linearyzacja odbywa się od tyłu do przodu . W tym przypadku,

  1. Pierwszy Shape zostanie zlinearyzowany, co wygląda następująco:

    Shape -> AnyRef -> Any

  2. Następnie Dotted jest linearyzowany:

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

  3. Następny w kolejce jest Blue . Zazwyczaj linearyzacja Blue będzie:

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

    ponieważ, w MyShape linearyzacji MyShape ( Krok 2 ), Shape -> AnyRef -> Any już się pojawił. Dlatego jest ignorowany. Zatem linearyzacja Blue będzie:

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

  4. Na koniec zostanie dodany Circle , a ostateczna kolejność linearyzacji będzie:

    Okrąg -> Niebieski -> Kolor -> Kropkowany -> Obramowanie -> Kształt -> AnyRef -> Dowolny

Ta kolejność linearyzacji decyduje o kolejności wywoływania metod, gdy super jest używana w dowolnej klasie lub cechy. Wywoływana jest implementacja pierwszej metody z prawej strony, w kolejności linearyzacji. Jeśli zostanie wykonane new MyShape().paint("Circle ") , zostanie wydrukowane:

Circle with Blue Color with Dotted Border 

Więcej informacji na temat linearyzacji można znaleźć tutaj .



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow