Scala-traits
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コードで*継承*する場合は、抽象クラスを使用します。
- コンパイルされた形式で配布する予定で、外部のグループがそれを継承するクラスを作成することを期待する場合は、抽象クラスを使用することをお勧めします。
- 効率が非常に重要な場合は、クラスを使用するようにしてください。