Scala-traits

提供:Dev Guides
移動先:案内検索

Scala-特性

トレイトはメソッドとフィールドの定義をカプセル化し、それらをクラスに混ぜて再利用できます。 各クラスが1つのスーパークラスのみから継承する必要があるクラス継承とは異なり、クラスは任意の数の特性を混在させることができます。

特性は、サポートされているメソッドのシグネチャを指定することにより、オブジェクトタイプを定義するために使用されます。 Scalaでは、特性を部分的に実装することもできますが、特性にはコンストラクタパラメータがない場合があります。

特性定義は、キーワード trait を使用することを除いて、クラス定義のように見えます。 以下は、特性の基本的な構文例です。

構文

trait Equal {
   def isEqual(x: Any): Boolean
   def isNotEqual(x: Any): Boolean = !isEqual(x)
}

この特性は、2つのメソッド isEqual および isNotEqual で構成されています。 ここでは、別のメソッドに実装があるisEqualの実装は提供していません。 特性を拡張する子クラスは、実装されていないメソッドの実装を提供できます。 したがって、特性は、Javaで* abstractクラス*を持っているものと非常に似ています。

特性 Equal の例に、2つのメソッド* isEqual()および isNotEqual()が含まれると仮定します。 特性 *Equal には、* isEqual()という実装メソッドが1つ含まれているため、ユーザー定義クラス *Point が特性 Equal を拡張する場合、 Point クラスの* isEqual()*メソッドへの実装を提供する必要があります。

ここでは、次の例で使用されるScalaの2つの重要なメソッドを知る必要があります。

  • obj.isInstanceOf [Point] objのTypeとPointが同じであることを確認することはできません。
  • obj.asInstanceOf [Point] は、オブジェクトobj型を取得して正確にキャストすることを意味し、Point型と同じobjを返します。

次のプログラム例を試して、特性を実装してください。

trait Equal {
   def isEqual(x: Any): Boolean
   def isNotEqual(x: Any): Boolean = !isEqual(x)
}

class Point(xc: Int, yc: Int) extends Equal {
   var x: Int = xc
   var y: Int = yc

   def isEqual(obj: Any) = obj.isInstanceOf[Point] && obj.asInstanceOf[Point].x == y
}

object Demo {
   def main(args: Array[String]) {
      val p1 = new Point(2, 3)
      val p2 = new Point(2, 4)
      val p3 = new Point(3, 3)

      println(p1.isNotEqual(p2))
      println(p1.isNotEqual(p3))
      println(p1.isNotEqual(2))
   }
}

上記のプログラムを Demo.scala に保存します。 このプログラムをコンパイルして実行するには、次のコマンドを使用します。

コマンド

\>scalac Demo.scala
\>scala Demo

出力

true
false
true

価値クラスと普遍的特性

値クラスは、ランタイムオブジェクトの割り当てを回避するためのScalaの新しいメカニズムです。 これには、 val パラメーターが1つだけのプライマリコンストラクターが含まれます。 var、val、ネストされたクラス、特性、またはオブジェクトを許可されていないメソッド(def)のみが含まれます。 値クラスを別のクラスで拡張することはできません。 これは、AnyValで値クラスを拡張することで可能になります。 実行時のオーバーヘッドのないカスタムデータ型の型安全性。

重量、身長、電子メール、年齢などの値クラスの例を見てみましょう。 これらすべての例で、アプリケーションにメモリを割り当てる必要はありません。

特性の拡張が許可されていない値クラス。 値クラスが特性を拡張できるようにするために、 Any を拡張する universal traits が導入されています。

trait Printable extends Any {
   def print(): Unit = println(this)
}
class Wrapper(val underlying: Int) extends AnyVal with Printable

object Demo {
   def main(args: Array[String]) {
      val w = new Wrapper(3)
      w.print()//actually requires instantiating a Wrapper instance
   }
}

上記のプログラムを Demo.scala に保存します。 このプログラムをコンパイルして実行するには、次のコマンドを使用します。

コマンド

\>scalac Demo.scala
\>scala Demo

出力

Wrapperクラスのハッシュコードが表示されます。

Wrapper@13

特性を使用する場合

確固たるルールはありませんが、考慮すべきガイドラインはほとんどありません-

  • 動作が再利用されない場合は、それを具体的なクラスにします。 結局、再利用可能な振る舞いではありません。
  • 無関係な複数のクラスで再利用される可能性がある場合は、それを特性にします。 特性だけがクラス階層の異なる部分に混在することができます。
  • Javaコードで*継承*する場合は、抽象クラスを使用します。
  • コンパイルされた形式で配布する予定で、外部のグループがそれを継承するクラスを作成することを期待する場合は、抽象クラスを使用することをお勧めします。
  • 効率が非常に重要な場合は、クラスを使用するようにしてください。