java経験者のScala入門メモ [ケースクラス、オブジェクト]
#オブジェクト
ケースクラスの次は コンパニオンオブジェクトとかでも出てくるオブジェクトというものを説明します
##オブジェクトの特徴
シングルトンなオブジェクトをscalaでは生成できて 静的メンバを記述していく(javaでいうstatic)
##オブジェクトの定義
オブジェクトはクラスと似ていて object object名{} の様に定義します
object Test{
def method(num:Int) = n+1
val a = 1
val b = 2
}
オブジェクトのメンバにアクセスしたいときは
//object Test{...}を仮定
Test.method(1)//return -> 2
Test.a // get -> 1
Test.b // get -> 2
##オブジェクトは値
オブジェクトは値として扱える(型はobject名.type)ので変数や関数に渡したり、戻り値にすることが出来る
object Test{
def method = 1
}
val test: Test.type = Test
test.method
これ以外にも
def function(test:Test.type) = test.method
def function2 = Test
みたいな定義方法もできます
##オブジェクトと継承
オブジェクトにクラスを継承させることはできても オブジェクトをオブジェクトやクラスに継承させることはできません java風に言うならスーパークラスにはなれない
##内部オブジェクト
オブジェクトの中にオブジェクトを作る場合
object A{
object B{
val b = 1
}
}
//bにアクセスするなら
A.B.b
クラスの中にオブジェクトを定義するとき
class A{
object B{
val b = 1
}
}
val a1 = new A
val a2 = new A
a1.B.b = 2
a2.B.b//get -> 1
##コンパニオンオブジェクト
コンパニオンオブジェクトとは「同じソース、パッケージ、クラス内で定義された同名オブジェクトのことで、お互いにプライベートなメンバにアクセスできるやつ」
#ケースクラス
ケースクラス、以前のクラスとは違うタイプのクラスがまだあるのかと思いますが やはり、クラスといえばただクラスであるようにjava経験者には馴染みにくいものかもしれません
##ケースクラスとは
ざっくり言ってしまえば、以下の様に書ける classの前にcaseをつけたクラスのことです
case class TestCaseClass(num:Int)
これだけではなにかわからないのでケースクラスの特徴についてまとめます ケースクラスの特徴は大きく5つあります
・インスタンス生成newを省略できる ・基本コンストラクタ引数(前回の記事、クラス、コンストラクタを参照)にすべてvalがついた状態になる ・パターンマッチに使える(なんじゃそりゃ) ・コンパニオンオブジェクト(後述)が生成される ・便利なメソッドが生成される(これメイン)
##生成される便利なメソッド達
###apply
applyメソッドはインスタンスを生成します さっき言ってたcaseクラスの特徴と矛盾するじゃねーかというツッコミをもらいそうですが caseクラスは完全にnewを省略してインスタンスを生成しているのではなく インスタンスを生成する為にapplyメソッドを呼び出し、インスタンスを生成しているらしいです
例えばこう使う
case class Person(name:String,age:Int)
val person = Person.apply("me",20)
//コンストラクタ引数として渡したいものをapplyに渡す
###copy
copyメソッドは現在生成してある既存のインスタンスをベースに 新しいインスタンスを生成できるメソッドです
例えばこう使う
case class Person(name:String,age:Int,from:String)
//applyでインスタンス生成
val p1 = Person.apply("me",20,"first instance")
//copyでp1ベースの新しいインスタンスを生成
val p2 = p1.copy(from = "second instance")
//fromだけ変えて後は同じ
###equalsと==、canEqual
equalsメソッドは基本コンストラクタ引数の値で参照が等しいか判断するメソッドです
a.equals(b)と書くことで使用できますが(case class aとcase class bを仮定した場合) ==演算子でも同じ判定が行えるので a == bとも書けます
実際につかってみるとこんな感じ
case class Person(name:String,age:Int)
val p1 = Person.apply("me",20)
val p2 = p1.copy()
val p3 = p1.copy(name = "you")
p1 == p2 //return -> true
p1 == p3 //return -> false
###ここまで踏まえてcanEqual
canEqualはインスタンスが同じクラスから生成されたものであるか判定するメソッドです 上のインスタンスが存在すると仮定すると
p1.canEqual(p2)
p1.canEquals(p3)//どれもreturn -> true
case class PersonFake()
val p4 = PersonFake.apply()
p4.canEquals(p1)//return -> false
###ここから意味不明なcurried、tupled
ここまで来るとさすがに意味不明感がすごく この分野の説明だけでは説明しきれないのがこの3つ 厄介すぎる…
tupledはtupleに関することだとはなんとなくわかると思います pythonユーザーであれば尚更しっくりくるかもしれません
pythonユーザーにわかるように言うなら コンストラクタ引数をtupleで取るように変換するメソッドのことです
pythonユーザーはこれで理解できるかもしれませんが javaユーザには正直つらいものが見えてくる
###まずtupledから まずtupledから説明しちゃいましょう、まだ軽いので
多分Scala入門でもあとから出てくるとは思いますが タプル(tuple)とは型が違う値でも関係なしに格納出来る配列、コレクション、リストのようなものです
それをコンストラクタ引数にしてしまうのでこうなります
case class Person(name:String,age:Int)
val change = Person.tupled//これで引数をタプルにしてしまう
val tuple = ("me",20)//これがタプル()で囲む
//引数がタプルになったクラスに↑のタプルを渡す
change(tuple)
こういうものなのです(書いてる本人もどこで使うかあまりわかってない)
###つぎにcurried これがjavaユーザーにとってケースクラスを理解する上での山場になるでしょう
まずcurriedメソッドはapplyメソッドをカリー化するメソッドです この時点で書いてる本人も理解してません
なんでもケースクラス以外でもカリー化できるらしく それがとても混乱しますね
この記事を書いている間に色々調べた結果、なんとなくわかったのでざっくりいうと 引数が複数あるものを引数がひとつのものに変えるという動作をカリー化というらしいです
ちなみにScalaユーザーズグループのチャットで聞いたとき 「case classを定義するとobject クラス名というコンパニオンオブジェクトが自動生成されそれがFunctionNなのでcurriedメソッドが使える、逆にそれを自分で定義してしまうとFunctionNにならずcurriedメソッドが無いからカリー化できない」ということもあるらしいです
case class Person(name:String,age:Int,from:String)
val curried = Person.curried//カリー化したものをcurriedに代入
val function = curried("me")(20)//先にnameとageを決めてしまう
function("curried complete")//これでクラスのコンストラクタ引数を一つにできた
ということらしいです
###コンパニオンオブジェクト
さっきcurriedのところでも少し触れた様にケースクラスを定義すると コンパニオンオブジェクトというものが同時に定義されます
詳しく話していくとまた複雑になってしまうのでざっくり言うと コンパニオンオブジェクトとは「同じソース、パッケージ、クラス内で定義された同名オブジェクトのことで、お互いにプライベートなメンバにアクセスできるやつ」と覚えておいてください
###その他メソッド
ここでは紹介しきれなかったメソッドの中に hashCode(基本コンストラクタで受け取った値からハッシュ値作成)とかtoString(クラス名と基本コンストラクタで受け取った値を返す)などなどあります
詳しくは自分がこの記事を書くのに参考にしているこのサイトを参照してもらえると詳しくわかると思います