java経験者のScala入門メモ [ケースクラス、オブジェクト]

More than 1 year has passed since last update.

はじめに

今回はさらっと終わらせてしまうつもりでしたが、ケースクラスが予想以上に重そうなので
ケースクラスとオブジェクトに関することだけまとめます

ケースクラス

ケースクラス、以前のクラスとは違うタイプのクラスがまだあるのかと思いますが
やはり、クラスといえばただクラスであるように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(クラス名と基本コンストラクタで受け取った値を返す)などなどあります

詳しくは自分がこの記事を書くのに参考にしているこのサイトを参照してもらえると詳しくわかると思います

オブジェクト

ケースクラスの次は
コンパニオンオブジェクトとかでも出てくるオブジェクトというものを説明します

オブジェクトの特徴

シングルトンなオブジェクトを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

コンパニオンオブジェクト

コンパニオンオブジェクトとは「同じソース、パッケージ、クラス内で定義された同名オブジェクトのことで、お互いにプライベートなメンバにアクセスできるやつ」

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.