以下のように考えれば良い。
例えば遺伝子のクラスは以下のように定義できる。
// 遺伝子クラスの定義
class Gene(val name:String, val chr:String, val start:Int, val end:Int, val strand:Strand)
valをパラメータ名に付けると、
val g = new Gene("gene1", "chr1", 10000, 20000, Strand.Forward)
g.name
とクラスの外部からパラメータにアクセスできるようになる。何もつけないとクラスの内部でしかパラメータにアクセスできない。Javaではパラメータにアクセスするためのgetter/setterを用意する、あるいはパラメータにpublic final
と付けるのが推奨されていたが、Scalaではパラメータにはvalを使ってimmutableにして副作用を避けるのが基本で、そうしたプログラミングが苦にならないよう配慮されている。
遺伝子を定義したらExon, Intronなども同じように定義できる。
// Exon
class Exon(val chr:String, val start:Int, val end:Int, val strand:Strand)
遺伝子もexonもゲノム座標中の区間として考えると同類なので、共通部分をGIntevalとして抽出してみる。
// genome中の区間[start, end)を表すクラス
class GInterval(val chr:String, val start:Int, val end:Int, val strand:Strand)
GIntevalクラスを継承するようにしてGene, Exonを書き換える。
git commit
しておくとよい。commit前に変更を始めてしまった場合は、git stash
-> git stash branch (new branch name)
のコンボ。Gene, ExonをGIntervalから継承させる。
class Gene(name:String, chr:String, start:Int, end:Int, strand:Strand)
extends GInteval(chr, start, end, strand)
class Exon(chr:String, start:Int, end:Int, strand:Strand)
extends GInteval(chr, start, end, strand)
Gene, Exonのコンストラクタの引数からvalが消えている。こうすると、親クラスと同名のパラメータが二重定義されないように、Scalaのコンパイラが頑張ってくれる。(変数を上書きしたい場合は、override
を付ける)
入力量はさほど減っていないが、これでGene, Exonに共通する操作はGIntevalで定義すればよくなった。たとえば、区間の交差を判定するメソッドをGIntervalに追加すると、Gene, Exonで共通に使えるようになる。
class GInterval(val chr:String, val start:Int, val end:Int, val strand:Strand) {
def intersectWith(other:GInterval) = {
chr == other.chr && start < other.end && other.start <= end
}
}
交差する区間をsweepする例で定義したクラスも、GIntervalから派生したクラス全般に適用できる。