Posted at

java経験者のScala入門メモ [トレイト、メソッド]

More than 3 years have passed since last update.


今回はトレイトとメソッド

タイトルどおり、今回はトレイトとメソッドについて

トレイトは実はjavaユーザには馴染み深い内容だったりする

メソッドはどちらかというと使い方より、仕組みであったりScalaでの定義であったり

知っておくべきではあるけど、つまらない内容になりそうな感じ


trait(トレイト)

trait(トレイト)はjavaユーザーには馴染み深いものです

なぜならScalaでのtraitはJavaでのinterfaceとほとんど同じだからです

ちょっと余談ですが本当にinterfaceとそっくりで

interfaceをいくつも実装することは出来ても、クラスの多重継承は出来ないという点も同じです

逆に何が違うかというと

javaのinterfaceには出来なかったメソッドに実装をもたせることが出来るという点です


トレイトの定義

定義はこれまで紹介したものの様に「trait」で宣言し

メソッドは戻り値の型を省略した場合はUnit型となるところに注意が必要です


trait Interface{
//javaでいう普通のinterfaceと同じでメソッド定義だけの場合
def method1(n:Int):Int

//戻り値の型がUnitに型推論される
def method2(n:Int)

//戻り値(ここではメソッドの実装)を持てる
def method3(n:Int) = n + 1
}


トレイトの実装とミックスイン

まずミックスインとはtraitを複数実装できるということです

これを踏まえた上でどうやって、トレイトを実装させるかというと

withキーワードを使用します

ただ少しややこしいのはクラスを継承しない場合(traitを実装したいクラスにスーパークラスがない場合)extendsの後に一つ目のtraitを記述し、withでつなげていきます

なのでこうなります


trait A{
def method1(n:Int) = n + 1
}

trait B{
def method2(n:Int) = n + 2
}

class C{
def method3(n:Int) = n + 3
}

class D extends C with A with B{
//こういう感じでwithでつなげていく
//Cクラスを継承しないならclass D extends A with Bとなる
def method1(n:Int) = n + 1
def method2(n:Int) = n + 2
def method3(n:Int) = n + 3
//実装したtraitのメソッドは必ず記述しなければならない
//ただしメソッドの実装があるものは記述しなくて良い
}

ちなみに継承関係でtraitにtraitを継承させることも可能です(Javaと同じ)

そして、実装したトレイトのメソッドは必ず記述しないと行けません(メソッドの実装を持たないものは)


オーバーライド

Scalaでメソッドのオーバーライドをするとき@Overrideのアノテーションをつけるのでは無く

overrideキーワードをつけます


trait A{
def method(n:Int):Int
}

class B extends A{
override def method(n:Int):Int = n+1
}


トレイトと初期化

ここが少し厄介

デフォルト値という概念があるが故にココがめんどくさい

以下のコードを見てください


trait A{
val a:Int
println(a)
}

class B extends A{
val a = 1
}

これの実行結果は何でしょうか?

まずtraitに直で処理書けるの!?ってところがすでにきてますけど

出来るんです、そうScalaならね

これを実行できるとして、実行結果は1か別の値か

実はこれ「0」が出力されるんです

というのも、この場合トレイトのメンバが初期化されていない扱いになり、Int型のデフォルト値が返るためです

トレイトを実装するクラスで定義する値を扱うためには事前定義という動作が必要になり

こう書きます


trait A{
val a:Int
println(a)
}

class B extends{
val a = 1
} with A

class クラス名 extends {

//事前定義

} with トレイト名

そしてこれと関連してトレイトには変数の初期値を持たせることも出来ます


トレイトのインスタンス

トレイトはインスタンスを作ることが出来ます

ただ、注意が必要で


trait A{
val a:Int
}

というトレイトを仮定した時

new A とは生成できません

なぜならトレイト自身が実装されていないからです

つまり、トレイトでインスタンスを生成するにはそれ自身が実装されている必要があります

new A と生成したいのなら

newを使用して、トレイトのインスタンスを実装したい場合はトレイトの実装をする必要があり以下の様に書けます

//上のtrait Aを仮定
val a = new A{
val a = 1
}

a.a//get -> 1

この書き方はミックスインが適応できて

trait A 以外にもB,Cがあるとするなら

new A with B with C{

....

}

のようにも書けます


メソッド

これはほとんどjavaと同じ部分があるので比較的簡単です


オーバーライド、オーバーロード

オーバーライドはトレイトのところで書いた様に

@Overrideではなくてoverrideを文頭に置きます

オーバーロードに関してはjavaと同じように実装できます


applyメソッド

ケースクラスでも登場したapplyメソッド

それに関して、クラスやオブジェクトにapplyメソッドを実装した場合、applyを省略できます

class A(n:Int){
def apply(_n;Int) = n * _n
}

val a = new A(1)
a(2)//こういう感じで


ドットの省略

今まではわかりやすいように.を使ってメソッドを呼び出してきましたが

実はこれは省略できて


class a(n:Int){
def method(_n:Int) = n+_n
}

val a = new a(2)
a a(2)//return -> 4

この書き方は中間記法といいます