Mixin
beginner's guideの「7 Mixins」を読みました。Scalaでは、RubyのようなMixinが使えます。
- イメージとしてはtraitと呼ばれるAbstractなClassを複数継承する感じ。
- 継承したクラスでは、traitで定義された実装を持つメソッドを使うことができる。
- また、traitで定義された実装を持たないメソッドを実装する必要がある。
// Mixinするメソッドを定義したtrait trait Comparable { // 実装を持たないメソッド。 // 派生クラスはこれを実装する必要がある。 def compare(that:Any):int // 以下のメソッドは実装を持つ。 def >(that:Any):boolean = compare(that) > 0 // Anyは「すべての型」を示す def ===(that:Any):boolean = compare(that) == 0 def <(that:Any):boolean = compare(that) < 0 def >=(that:Any):boolean = compare(that) >= 0 def <=(that:Any):boolean = compare(that) <= 0 } // Mixinするメソッドを定義したtrait その2 trait Named { def getName:String def tellYourName = println( "my name is " + getName + "!" ) } // ComparableとNamedをMixinをしたクラス class Kitten( name:String, age:int ) // traitを継承。withでつないで複数追加できる。 extends Comparable with Named { def getName = name def getAge = age def compare(that:Any):int = { // インスタンスの型をチェック if (!that.isInstanceOf[Kitten]) { error("that isnot Kitten.") } // キャスト val o = that.asInstanceOf[Kitten] return getAge - o.getAge } } // Mixinしたクラスを使うサンプル。 object MixinSample { def main(args: Array[String]) { var mii = new Kitten( "mii", 1) var tora = new Kitten( "tora", 1) var shiro = new Kitten( "shiro", 2) mii.tellYourName tora.tellYourName println("mii > tora : " + (mii > tora).toString() ) // false println("mii < tora : " + (mii < tora).toString() ) // false println("mii === tora : " + (mii === tora).toString() ) // true println("mii >= tora : " + (mii >= tora).toString() ) // true println("mii <= tora : " + (mii <= tora).toString() ) // true println("mii > shiro : " + (mii > shiro).toString() ) // false println("mii < shiro : " + (mii < shiro).toString() ) // true println("mii === shiro : " + (mii === shiro).toString() ) // false println("mii >= shiro : " + (mii >= shiro).toString() ) // false println("mii <= shiro : " + (mii <= shiro).toString() ) // true } }
実行結果です。
my name is mii! my name is tora! mii > tora : false mii < tora : false mii === tora : true mii >= tora : true mii <= tora : true mii > shiro : false mii < shiro : true mii === shiro : false mii >= shiro : false mii <= shiro : true
もし、Kittenがcompareを実装していなければ、コンパイル時に以下のようなエラーになります。
MixinSample.scala:12: error: class Kitten needs to be abstract, since method compare in trait Comparable of type (Any)int is not defined class Kitten( name:String, age:int ) ^ one error found
これはいいなー。RubyでもMixinはできるけど、Mixinを使う側のクラス(上の例ではKitten)で実装が必要なAPIをチェックする仕組みはなかったはず。(そもそもそういうチェックはしないという設計思想なのかもだけど)。こういう型チェックはあってもいいなと思う。