読者です 読者をやめる 読者になる 読者になる
無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

Mixin

Scala

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をチェックする仕組みはなかったはず。(そもそもそういうチェックはしないという設計思想なのかもだけど)。こういう型チェックはあってもいいなと思う。