Scala Language
Eigenschappen
Zoeken…
Syntaxis
- kenmerk ATrait {...}
- klasse AClass (...) verlengt ATrait {...}
- klasse AClass breidt BClass uit met ATrait
- klasse AClass breidt ATrait uit met BTrait
- klasse AClass breidt ATrait uit met BTrait met CTrait
- klasse ATrait verlengt BTrait
Stapelbare modificatie met eigenschappen
Je kunt eigenschappen gebruiken om methoden van een klasse te wijzigen, door eigenschappen op een stapelbare manier te gebruiken.
Het volgende voorbeeld laat zien hoe eigenschappen kunnen worden gestapeld. De volgorde van de eigenschappen is belangrijk. Met behulp van verschillende volgorde van eigenschappen, wordt verschillend gedrag bereikt.
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
}
}
Merk op dat super
wordt gebruikt om de methode roll()
in beide eigenschappen aan te roepen. Alleen op deze manier kunnen we stapelbare modificaties bereiken. In het geval van stapelbare modificatie, wordt de methode-aanroepvolgorde bepaald door linearisatieregel .
Basiskennis van eigenschappen
Dit is de meest eenvoudige versie van een eigenschap in 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"
}
Aangezien er geen superklasse wordt verklaard voor eigenschap Identifiable
, wordt deze standaard uitgebreid van de klasse AnyRef
. Omdat er geen definitie voor getIdentifier
wordt gegeven in Identifiable
, moet de Puppy
klasse deze implementeren. Puppy
neemt de implementatie van printIdentification
van Identifiable
.
In de REPL:
val p = new Puppy("K9", "Rex")
p.getIdentifier // res0: String = Rex has id K9
p.printIndentification() // Rex has id K9
Het Diamond-probleem oplossen
Het diamantprobleem , of meervoudige overerving, wordt afgehandeld door Scala met behulp van eigenschappen, die vergelijkbaar zijn met Java-interfaces. Kenmerken zijn flexibeler dan interfaces en kunnen geïmplementeerde methoden bevatten. Dit maakt eigenschappen vergelijkbaar met mixins in andere talen.
Scala ondersteunt geen overerving van meerdere klassen, maar een gebruiker kan meerdere eigenschappen in een enkele klasse uitbreiden:
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
Hier grandChild
van zowel traitB
als traitC
, die beide op hun beurt erven van traitA
. De uitvoer (hieronder) toont ook de volgorde van prioriteit bij het oplossen van welke methode-implementaties het eerst worden genoemd:
C is a child of A.
B is a child of A.
This is the 'grandparent' trait.
Merk op dat, wanneer super
wordt gebruikt om methoden in de class
of trait
te roepen, lineariseringsregel een rol speelt om de hiërarchie van de oproep te bepalen. Linearisatieorder voor grandChild
is:
grandChild -> traitC -> traitB -> traitA -> AnyRef -> Any
Hieronder is nog een voorbeeld:
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!")
}
}
Dit programma drukt af:
*************
-------------
Hello World!
Linearisatie voor CustomPrinter
zal zijn:
CustomPrinter -> DelimitWithStar -> DelimitWithHyphen -> Printer -> AnyRef -> Any
Linearization
In het geval van stapelbare modificatie , rangschikt Scala klassen en eigenschappen in een lineaire volgorde om de methodeaanroephiërarchie te bepalen, die bekend staat als linearisatie . De linearisatieregel wordt alleen gebruikt voor die methoden waarbij de methode wordt aangeroepen via super()
. Laten we dit als voorbeeld beschouwen:
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)
}
}
Linearisatie gebeurt van achter naar voren . In dit geval,
First
Shape
wordt gelineariseerd, wat eruit ziet als:Shape -> AnyRef -> Any
Dan is
Dotted
gelineariseerd:Dotted -> Border -> Shape -> AnyRef -> Any
De volgende in de rij is
Blue
. Normaal is de linearisatie vanBlue
:Blue -> Color -> Shape -> AnyRef -> Any
omdat in de linearisatie van
MyShape
tot nu toe ( stap 2 )Shape -> AnyRef -> Any
al is verschenen. Daarom wordt het genegeerd. DeBlue
linearisatie zal dus zijn:Blue -> Color -> Dotted -> Border -> Shape -> AnyRef -> Any
Ten slotte wordt
Circle
toegevoegd en wordt de uiteindelijke lineariseringsvolgorde:Cirkel -> Blauw -> Kleur -> Gestippeld -> Rand -> Vorm -> AnyRef -> Any
Deze lineariseringsvolgorde bepaalt de volgorde van methoden wanneer super
wordt gebruikt in een klasse of eigenschap. De eerste methode-implementatie van rechts wordt aangeroepen, in de linearisatievolgorde. Als new MyShape().paint("Circle ")
wordt uitgevoerd, wordt deze afgedrukt:
Circle with Blue Color with Dotted Border
Meer informatie over linearisatie vindt u hier .