Javaのオブジェクト指向っぽい説明の例を、Scalaに書き換えてScalaを説明してみる。
Scalaはオブジェクト指向と関数型が融合された言語なので、ベースとしてJavaと似たオブジェクト指向(クラス)が使える。
Javaのインターフェースは、Scalaではトレイトで書く。
trait 政党 {
def 政策を作成する() : 政策
}
defでメソッドを定義する。(Scalaは関数型言語でもあるが、クラスやトレイト内に定義するのは関数ではなくメソッド。場面に応じて、メソッドは関数に(自動的に)変換して使われる)
Javaとは違い(Pascalと似て)、メソッドの戻り値の型(や変数の型)はコロンで区切って後ろに書く。
抽象メソッドの宣言はabstractキーワードをつける必要は無く、メソッド本体を記述しないだけ。
また、可視性は、何も書かない場合はpublicになる。
世界に一つしか無い官僚機構は以下のような感じで書ける。
object 官僚 {
def 政策を実施する(s: 政党) : 結果 = {
try {
val dummy = s.政策を作成する()
} catch {
case e: Exception =>
}
new 天下り先()
}
}
(トップレベルに書かれた)objectは、シングルトンオブジェクトとなる。つまりnewしなくても自動的にVM内に1つだけインスタンスが作られる。
(トップレベルでなくクラス内に書かれたobjectはクラスをインスタンス化する際にそのインスタンス毎に作られる(staticの代わりではない)ので注意)
メソッド本体は、戻り型の指定の後ろに「=」を付けてその後ろに書く。
各文の末尾のセミコロンは省略可能。Scalaでは普通は書かない。
メソッドの戻り値としては、波括弧でくくられたブロックの一番最後の値が返る。したがって「return」は書かない。(Javaと違って、メソッド内の途中でreturnで抜けることは基本的にしない)
try~catchの機能はJavaと同じだが、catch句の書き方がちょっと違う。(Scalaのmatch式の書き方に近い)
caseの「=>」の後ろに例外処理を書くのだが、官僚は握りつぶしている(何も処理しない)ので、例としては良くないな^^;
トレイトをミックスイン(「インターフェースを実装する」のと同じような意味)してクラスを書くには以下の様にする。
class 自党 extends 政党 {
override def 政策を作成する() : 政策 = new 我田引鉄()
}
class 共党 extends 政党 {
override def 政策を作成する() : 政策 = new 実現不可能な理想論()
}
メソッドをオーバーライドする際はoverrideを付ける。Javaでは@Overrideアノテーションだったが、Scalaでは言語内で規定されたキーワードとなっている。
(抽象メソッドをオーバーライドする際はoverrideを付けなくてもいい(付けないのが多数派だ)が、個人的には付けた方が良いと思っている。オーバーライドしていることが一目瞭然だし、親クラスのメソッド名を変えたときにコンパイルエラーになってくれるし)
メソッド本体が1つの式で収まる場合は、波括弧のブロックにする必要が無い。
(Scalaの構文としては、メソッド本体は1つの式を指定するだけ。複数の文を書きたい場合に波括弧のブロックを利用するという扱い)
インスタンスをnewで作ることはJavaと同じ。
ただし、コンストラクターの引数が無い場合は丸括弧も省略できる。
val 元祖自党 = new 自党()
var 亀下新党 = new 自党
var 石井新党 = new 自党
var 橋原新党 = new 自党
変数はvarまたはvalで定義する。
valは、Javaでのfinal変数と同じ。つまり、valで定義した変数には他の値を再代入することが出来ない。変数が別のインスタンスを指すことが出来ないというだけで、変数内のインスタンスの中の値が書き換えられないわけではない。Scala(関数型の考え方?)としては、インスタンス内が書き換えられないようにクラス(イミュータブルなクラス)を作るのが望ましい。
イミュータブルなクラスでは、インスタンス内の一部を書き換えたい場合は、書き換えずに新しいインスタンスを生成する形となる。
今回の政党の機能としては扱っていないが、政党間で党員の移動(自党から民党への移籍等)は非常に頻繁に起こるので、インスタンス内の党員の一覧は書き換え可能な方がやりやすいだろう。
(個人的には、現在のScalaがJavaVM上で動いている以上はインスタンスを大量に生成するのは効率が悪いので、大量に書き換えが発生しそうな用途ならミュータブルにした方がいいと思う。ぜひ、Scalaにはイミュータブルでも効率の良いScalaVMを作って欲しいw)
あと、自分は手続き型言語歴が長いので、変数へ「代入する」という言葉を使うが、Scalaでは「束縛する(bind?)」と言うのが正しいっぽい。
変数名やメソッド名などに値を束縛する(割り当てる・固定する)というニュアンスなんだと思う。
既存のクラスを継承したクラスは以下の様に書く。
class 民党 extends 自党
独自のメソッドやフィールドを定義しない場合は、クラス定義の波括弧が省略できる。
コンストラクターの引数で渡された値をフィールドに保持するだけの場合、クラス名の直後に引数(フィールド定義を兼ねる)を指定するような書き方が出来る。
class 委譲政党(protected val s: 政党) extends 政党 {
override def 政策を作成する() = s.政策を作成する() //他の政党に委譲する
}
val 公党 = new 委譲政党(元祖自党)
val 社党 = new 委譲政党(元祖自党)
メソッドの戻り値の型がメソッド本体から推測できる場合は、defの型の指定は省略することが出来る。
ちなみに、Javaと同じような書き方をすると、以下の様にくどくなる。
class 委譲政党 extends 政党 {
protected var s:政党 = null
def this(s: 政党) = {
this()
this.s = s
}
override def 政策を作成する() = s.政策を作成する() //他の政党に委譲する
}
Scalaのクラスでは基本コンストラクターというものが必ず出来る。クラス名の直後に引数を書くとそれが基本コンストラクターの引数となる(省略された場合は引数なしのコンストラクターとなる)。
それ以外のコンストラクター(補助コンストラクター)はthisという名前のメソッドを定義するような書き方をする。
上記のthisメソッドでは、先頭でthis()を呼び出しているが。def thisで作った補助コンストラクターは必ず(自分より前に定義された)他のコンストラクターを呼び出す必要がある。ここら辺はJavaのコンストラクターとは考え方が違うところ。
基本的にはJavaのオブジェクト指向で出来る事はScalaでも出来るが、Scala独自の考え方で異なっている部分もある。
Scalaの勉強としては、次のステップはList操作(ScalaではSeqというクラス)を勉強するのが良いと思う。関数型を生かした便利メソッドがいっぱい出てくるがw、foreachが基本でfilterやmap辺りがまず重要。
※コメント投稿者のブログIDはブログ作成者のみに通知されます