Scala Language
形質
サーチ…
構文
- 形質ATrait {...}
- クラスAClass(...)はATrait {...}を拡張します
- クラスAClassはATclassでBClassを拡張します
- クラスAClassがATraitをBTraitで拡張
- クラスAClassは、CTraitでBTraitとATraitを拡張します。
- クラス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
クラスからAnyRef
されます。 Identifiable
にはgetIdentifier
の定義がないため、 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
ダイヤモンドの問題を解決する
ダイヤモンドの問題や多重継承は、Javaインタフェースに似たTraitsを使ってScalaによって処理されます。形質はインターフェースよりも柔軟性があり、実装されたメソッドを含めることができます。これにより、他の言語のミックスインと同様の特性が得られます。
Scalaは複数のクラスからの継承をサポートしていませんが、ユーザーは1つのクラスで複数の特性を拡張できます:
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
から継承しtraitA
。出力(下)には、最初に呼び出されるメソッド実装を解決する際の優先順位も示されています。
C is a child of A.
B is a child of A.
This is the 'grandparent' trait.
super
がclass
またはtrait
メソッドを呼び出すために使用されるとき、呼び出し階層を決定するために線形化ルールが有効になることに注意してください。 grandChild
線形化の順序は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 - > Printer - > AnyRef - > Any
線形化
スタック可能な変更の場合、スカラは、線形化と呼ばれるメソッド呼び出しの階層を決定するために、クラスと特性を線形の順序で配列します。線形化ルールは、 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)
}
}
線形化は前後に発生します 。この場合、
First
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
線形化の詳細については、 ここを参照してください 。