今回はオブジェクト指向を試してみるよ!
Scalaはオブジェクト指向もできるんだ!
ソースでの話がメインだよ。
だからオブジェクト指向プログラミングだ。
Javaだと冗長だけど、Scalaだとコンストラクタ・フィールドの
組み合わせで凄いシンプルになるんだ。
JavaBeans、いわゆる情報格納役で見てみよう。
Javaのソースコード
public class User {
private final long id;
private final String firstname;
private final String lastname;
public User(long id, String firstname, String lastname) {
this.id = id;
this.firstname = firstname;
this.lastname = lastname;
}
public long getId() {
return id;
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
public String getFullname() {
return this.firstname + " " + this.lastname;
}
}
Javaプログラマーから見ると、普通のソースだと思う。
説明は省くよ!これをScalaにしてみよう。
Scalaのソースコード
class User(val id: Long, val firstname: String, val lastname: String) {
def getFullname() = this.firstname + " " + this.lastname
}
なんということでしょう!
Javaだとダラダラ長かったソースが、わずか3行になってしまった。
クラスの書き方
- クラス名は、
class
の後に書くんだ! - 引数は、クラス名の次に書くんだ!
- メソッドは、
{...}
の中にdef
で書くんだ!。
あれ?コンストラクタ宣言は?フィールド宣言は?
基本コンストラクタ
Scalaではクラス全体が__基本コンストラクタ__というモノになる。
つまりコンストラクタはもう宣言してあるんだ。
フィールド宣言
基本コンストラクタの引数に、val
(またはvar
)を付ければ
そのままフィールドになる。
だからJavaでは長かったUserクラスが、Scalaでは3行で実現できたんだ。
フィールドへのアクセス
インスタンス.フィールドで可能だ!
val user = new User(1, "源次", "富樫")
user.firstname
直接フィールドにアクセスするのって、カプセル化の観点からいけないのでは?って。
確かにそうなんです。。。
今回はフィールドをval
にしているということで、許して下さい。
もちろん、フィールドに直接アクセスできないようにすることも可能だよ。
class User(private val id: Long, private val firstname: String, private val lastname: String) {
def getFullname() = this.firstname + " " + this.lastname
}
val user = new User(1, "源次", "富樫")
println(user.id)
引数の型の前に、private
を宣言した。
これを実行すると、
$ scala User1.scala
User1.scala:6: error: value id in class User cannot be accessed in this.User
println(user.id)
^
one error found
となり。外からはid
フィールドにアクセスできないというエラーが出るよ。
これでgetter
を用意すれば、フィールドに直接アクセスしないで
フィールドの値を取得できるね!
次にこのソースを少し変えてみよう!
改変したScalaのソース
class User(val id: Long, val firstname: String, val lastname: String, val fullname:Fullname = new Fullname) {
def getFullname() = this.fullname.get(firstname, lastname)
}
class Fullname() {
def get(firstname: String, lastname: String) = firstname + " " + lastname
}
class FullnameInJapan() extends Fullname {
override def get(firstname: String, lastname: String) = lastname + " " + firstname
}
val user1 = new User(1, "源次", "富樫", new FullnameInJapan)
println(user1.getFullname())
val user2 = new User(2, "鯛雄", "松尾")
println(user2.getFullname())
今度は実行できるようにしてあるので、実行してみよう!
$ scala User2.scala
富樫 源次
鯛雄 松尾
出力結果だけど、user1
とuser2
でfirstname
、lastname
の順番が違っているよね。
コンストラクタのデフォルト値
Userクラスのコンストラクタを先程とは少し変えているよ。わかるかな?
class User(val id: Long, val firstname: String, val lastname: String, val fullname:Fullname = new Fullname) {
def getFullname() = this.fullname.get(firstname, lastname)
}
fullname
という引数を増やしているよ。型はFullname
だ。
そして = new Fullname
ってあるよね?これがコンストラクタのデフォルト値だ。
このデフォルト値は、fullname
が指定されない場合に使用する。
val user2 = new User(2, "鯛雄", "松尾")
これがデフォルト値を使う場合だね。
ところでこのfullname
は何に使われるかと言うと、
メソッドgetFullname
の処理として呼び出されるのだ。
オブジェクト指向的に言うと、フルネームを作成するための処理を委譲しているのだ。
これでちょっとオブジェクト指向ぽくなったんじゃない?
オブジェクト指向プログラミングでは使われる頻度が多い、
__Strategyパターン__ぽくしてみたよ。
クラスの拡張
クラスFullname
は次のようになっている。
class Fullname() {
def get(firstname: String, lastname: String) = firstname + " " + lastname
}
そして、クラスFullname
を__拡張__してクラスFullnameInJapan
を定義している。
class FullnameInJapan() extends Fullname {
override def get(firstname: String, lastname: String) = lastname + " " + firstname
}
extends
で拡張し、overrid
でメソッドを__オーバーライド__する。
Javaとそんなに変わらないんだ。
クラスFullnameInJapan
を使う場合は、以下のようになる。
val user1 = new User(1, "源次", "富樫", new FullnameInJapan)
これで、fullname
のデフォルト値を使わなくなる。
まとめ
今回はオブジェクト指向の中で、コンストラクタとフィールドに注力してみた。
Scalaでは冗長な部分を排除でき、単純に記述量が減り見通しが良くなることを
体で感じてくれたかな?
今回で、第一部を完了して、次章からScalaの世界で泳ぎ始めるよ!