サーチ…


構文

  • 形質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

ここで、 grandChildtraitBtraitC両方から継承していますtraitAから継承しtraitA 。出力(下)には、最初に呼び出されるメソッド実装を解決する際の優先順位も示されています。

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

superclassまたは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)
  }
}

線形化は前後に発生します 。この場合、

  1. First 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