今回は 抽出子 だよ。
こいつを使えるようになると、更にパターンマッチが便利になるんだ。
こんな感じで進めてみるよ!
抽出子とは
unapply
メソッドが定義されているオブジェクト のことです。
定義としては簡単だね!
このメソッドの役割は、受け取ったインスタンスを分解して、 何かを抽出する ことです。
パターンマッチでこのメソッドが使われることが多いです。
抽出子を使う
早速使ってみよう!
パターンマッチで使う
import scala.Option
class User(private val firstname: String, private val lastname: String)
object User {
def unapply(user: User) = Option((user.firstname, user.lastname))
def main(args: Array[String]): Unit = {
val user = new User("剛次", "赤石")
user match {
case User(firstname, lastname) => println(lastname + " " + firstname)
case _ => println("Not user.")
}
}
}
実行してみよう!
$ scalac User.scala
$ scala scala User
赤石 剛次
説明
まずUser
クラスを用意してみたぞ。
class User(private val firstname: String, private val lastname: String)
次にUser
クラスのコンパニオンオブジェクトを作り、この中にunapply
メソッドを用意した。
object User {
def unapply(user: User) = Option((user.firstname, user.lastname))
}
unapply
メソッドの決め事は、以下みたいなんだ。
- 引数は、抽出対象のインスタンス
- 戻り値は、抽出したモノを
Option
で包む
今回引数は、コンパニオンクラスのインスタンスにしてみた。
そして戻り値は、fristname
とlastname
にした。
最後に使う場所をUser
オブジェクトのmain
メソッドとして用意した。
def main(args: Array[String]): Unit = {
val user = new User("剛次", "赤石")
user match {
case User(firstname, lastname) => println(lastname + " " + firstname)
case _ => println("Not user.")
}
}
生成したUser
インスタンスのuser
をパターンマッチすると、case User(firstname, lastname)
でUser#unapply
が呼び出され、
抽出した値を変数firstname
とlastname
に束縛する。
こんな感じで 抽出子 を使うことができるんだ。
代入で使う
こんな形でも使えるぜ!さっきのUser
クラスがいる前提でだからな!
object User1 {
def main(args: Array[String]): Unit = {
val user = new User("剛次", "赤石")
val User(firstname, lastname) = user
println(lastname + " " + firstname)
}
}
先にパターンマッチで説明しちゃったけど、こっちの方が抽出子って感じがする。
抽出子って使いようによっては可能性が広がるね!
unapplySeqメソッド
unapplyにSeqが付いてるメソッド。ということは、もしかして。。。
そうです。可変長のオブジェクトからの抽出子となります。
Seqがついているのが気になったので、ScaladocでSet
とMap
を見てみた。やっぱりunapply
とunapplySeq
がいない。
つまり順番で抽出することがわかるね。
あとはさらっと流すよ。
val seq = Seq(1, 2, 3, 4)
val Seq(x, y , _*) = seq
抽出して変数x
, y
に束縛したね。
applyメソッド
基本編ではapply
メソッドについて触れていなかったので、ここで触れます。
まず以下のようなapply
メソッドを持つオブジェクトを用意する。
scala> object ApplyTest { def apply(message: String) { println(message) } }
このメソッドを呼び出すには、こうするよね?
scala> ApplyTest.apply("乱坊少佐")
乱坊少佐
でもApplyTest("乱坊少佐")
とするだけでも、apply
メソッドを呼びすことが可能なんだ!
scala> ApplyTest("乱坊少佐")
乱坊少佐
いわゆる、シンタックスシュガー(糖衣構文)ってやつだね。
こういう使い方は少ないのかな?ファクトリーメソッド的な感じで使うことが多いみたいなんだ。
例えばこんな感じ。
scala> class ApplyClass(val message: String); object ApplyClass { def apply(message: String): ApplyClass = { new ApplyClass(message) } }
defined class ApplyClass
defined module ApplyClass
scala> ApplyClass("乱坊少佐")
res0: ApplyClass = ApplyClass@43679ffd
まぁシンタックスシュガーということだけで、後は他のメソッドと変わらないからな!
ところで、今までもよく使っているオブジェクトがありました。それはなんでしょうか?
そうです。コレクション系です。軽くSeq
オブジェクトapply
メソッドについて見てみよう。
def apply[A](elems: A*): Seq[A]
Seq[A]
を返している。つまり今まで
Seq(1, 2, 3)
なんて書いていたのは、apply
メソッドを使っていたのです。
更にちなみに、引数のelems: A*
は複数を表す。もちろん0個の場合もOK。
正規表現の 直前の文字の0回以上の繰り返し と似ているね。
ケースクラス
apply
, unapply
共通な話なので、ここに記載する。
ケースクラスを作ると、apply
, unapply
メソッドが自動で実装されます。興味がある人は、見てみてね!
applyとunapplyの関係
関係ありそうだよね?
まず、常に両方を定義する必要はありません。必要な方だけ定義すれば良いです。
それは今までの例を見ればわかってもらえると思います。
両方を定義する場合はどうだろう?
実は 注入 と 抽出 の関係が存在している。
-
注入(injection)
-
apply
メソッド
-
-
抽出(extraction)
-
unapply
メソッド
-
と考えるんだ。
抽出は 何か を抽出し、注入は 何か を注入する。 何か を共通の存在にできるね。
必ずこの関係性を満たす必要はないけど、なるべく満たした方が良いなんてコップ本に書いてあった。
何か を共通のモノにできない場合は、設計を疑った方が良いのかもね。
まとめ
抽出子は使いこなすと強力な武器になりそうだろ?
ぜひ使ってみてね!
今回も
体で感じてくれたかな?