Scalaのobjectについてまとめてみた
今回はscalaのオブジェクトについて調べてみたので、どこまで理解しているかも含めブログに書いておきます。
ここは。。という点あればどんどん指摘お願いします。
・シングルトンオブジェクト
Scalaのclassとobjectの違いですが、classはいわゆる通常のクラスの同じです。
Scalaのクラスではstaticな変数やメソッドを定義することが出来ません。
その代わりに、object(シングルトン)を使います。objectキーワードによって定義したオブジェクトにメソッドやフィールドを持たせることができます。
またクラスと違い、コンストラクタを明示的に定義することはできません。
objectキーワードで通常のクラスのようにnewでインスタンスを生成しなくても、定義したオブジェクトのフィールドやメソッドを利用できます。
scala> class Performance(val capacity: Int, val display: Double, val weight: Int){ | override def toString = { | "capacity: %d GB display: %.1f inch weight: %d g" format(capacity, display, weight) | } | } defined class Performance scala> object GalaxyS3 { | val performance = new performance(32, 4.7, 200) | val brand = "Sumsung" | } defined object GalaxyS3 scala> GalaxyS3.performance res1: performance = capacity: 32 GB display: 4.7 inch weight: 200 g scala> GalaxyS3.brand res2: String = Sumsung
・コンパニオンオブジェクト
コンパニオンオブジェクトとは、同スコープ内(同一ファイル内・同ブロック内)で定義されたクラス名と同じ名前のシングルトンオブジェクトです。
それに対してコンパニオンオブジェクトが属しているクラスをコンパニオンクラスといいます。
scalaではstaticキーワードがないためクラスにstaticなメンバを持たせることができません。
なのでobjectキーワードによって定義したオブジェクトにメンバを持たせることで、クラス内部のprivateなメンバを参照できます。
では例を見ていきます。
class SmartPhone private(val color:String, val price:Int, maker:String){ def this(color:String, maker:String) = this(color, 500, maker) override def toString = { "color: %s, price: $%d, maker: %s"format(color, price, maker) } } object SmartPhone { def apply(color:String, price:Int, maker:String) = new SmartPhone(color, price, maker) } // Exiting paste mode, now interpreting. defined class SmartPhone defined object SmartPhone scala> SmartPhone("blue", 900, "Apple") res10: SmartPhone = color: blue, price: $900, maker: Apple
↑コンパニオンオブジェクトSmartPhoneからコンパニオンクラスのメンバにアクセスできていることが確認できます。
この逆も可能で、コンパニオンオブジェクトのprivateメンバにコンパニオンクラスからアクセスも可能です。
またシングルトンオブジェクトは最初にアクセスされた時に初期化されます。
実際にコードで見てみましょう。
class SmartPhone private(val color:String, val price:Int, maker:String){ def this(color:String, maker:String) = this(color, 500, maker) override def toString = { "color: %s, price: $%d, maker: %s"format(color, price, maker) } } object SmartPhone { println("初期化されました") def apply(color:String, price:Int, maker:String) = new SmartPhone(color, price, maker) } // Exiting paste mode, now interpreting. defined class SmartPhone defined object SmartPhone scala> SmartPhone 初期化されました res11: SmartPhone.type = SmartPhone$@1eb6749b
↑はシングルトンオブジェクトにアクセスされた際に初期化されていることが分かります。
このようにScalaではstaticなメンバを作成できないため、同様のことをしたい場合はobjectキーワードで、コンパニオンオブジェクトとコンパニオンクラスのお互いのprivateなメンバーにアクセスします。
参考サイト
http://dany1468.tumblr.com/post/308257173/scala
http://aya-eiya.hateblo.jp/entry/20110219/1298121073
Scalaの継承、abstract、mixinについて
Scalaの継承、abstract、mixinについて調べたのでブログに書いておきます。
継承について
まずは継承から説明していきます。
クラスを継承するにはextendsキーワードをつけます。
多重継承はできません。継承できるクラスは1つだけです。
finalで定義されたクラスは継承できません。
final class ClassName
abstractについて
次はabstractクラスを説明していきます。
abstractクラスはabstractキーワードを使って定義します。
abstract class AbstractClassName{ /* 抽象メンバーを定義 普通のメンバも定義可能 */ }
abstractクラスはメンバーの具体的な定義をせず、継承した子クラスで具体的に定義するとき時に利用します。
・抽象メンバーにはabstractキーワードが不要(abstractクラスのメンバーはデフォルトが抽象メンバーになる)
・メソッド以外にもフィールドを抽象化できる
scala> abstract class Greet { | //抽象フィールド | val value: String | //抽象メソッド | def morning(arg:String): String | } defined class greet scala> class Morning extends Greet { | //抽象フィールドの実装 | val value = "Good morning" | //抽象メソッドの実装 | def morning(arg: String) = "Good moning " + arg | } defined class Morning scala> val am = new Morning am: morning = morning@36d64342 scala> am.Morning("scala") res1: String = Good moning scala scala> am.value res2: String = Good morning
ちなみに抽象クラスを継承したクラスは、この抽象メソッドを必ずオーバーライドしなければいけません。(オーバーライドしないとコンパイルエラーに)
scala> class Morning extends Greet { | val value = "Good morning" | } <console>:8: error: class Morning needs to be abstract, since method morning in class greet of type (arg: String)String is not defined class Morning extends Greet { ^
また抽象クラスに対してあまり使うイメージが湧かないという方は↓のページを見たらよりabstractクラスのユースケースがイメージできると思います。
http://www.itsenka.com/contents/development/java/abstract.html
トレイトとミックスインについて
トレイトはtraitキーワードによって定義します。通常のクラスのようにコンストラクタを持つことはできません。
抽象クラスのように抽象メンバーを定義することができます。
抽象メンバはトレイトがミックスインされる先のクラスで実装される必要があります。
ではトレイトの例をみていきます。
scala> trait ToSellPrice{ | //抽象フィールド | val price:Int | def toSellPrice = price * 0.6 | } defined trait ToSellPrice scala> class Iphone(color:String, capacity:Int ) extends ToSellPrice { | //抽象フィールドを定義しないとコンパイルエラーになる | val price = 900 | } defined class Iphone scala> val myPhone = new Iphone( "silver", 32 ) myPhone: Iphone = Iphone@1996cd68 scala> myPhone.toSellPrice res1: Double = 540.0
今回は継承しているクラスがないのでextendsでトレイトをミックスインしていますが、継承クラスがある場合はwith トレイト名というようにミックスインします。
複数トレイトをミックスインしたい場合はwith トレイト名 with トレイト名というように記述します。
ここで思ったのが、トレイトと抽象クラスってどっちを使えばいいの?ってことです。
やはり同じようなことを思った人がいるようで、それは↓の記事になります。
http://modegramming.blogspot.jp/2012/12/scala-tips_20.html
この違いはもっとコードを書くようになったら明確に分かると思うので、また別の機会にまとめてみます。
- 作者: Martin Odersky,Lex Spoon,Bill Venners,羽生田栄一,水島宏太,長尾高弘
- 出版社/メーカー: インプレスジャパン
- 発売日: 2011/09/27
- メディア: 単行本(ソフトカバー)
- 購入: 12人 クリック: 235回
- この商品を含むブログ (45件) を見る
- 作者: 中村修太
- 出版社/メーカー: 中村 修太
- 発売日: 2013/03/02
- メディア: Kindle版
- 購入: 1人 クリック: 27回
- この商品を含むブログ (4件) を見る
ScalaのMapコレクションについて
今回もScalaの代表的なコレクションMapについて調べたのでブログに書いておきます。
Mapはデフォルトがimmutableで、mutablle.Mapを使うことでmutableなMapを作成することも可能。
mutableなMap
scala> import scala.collection.mutable.Map import scala.collection.mutable.Map scala> val treasureMap = Map[ Int, String ]() treasureMap: scala.collection.mutable.Map[Int,String] = Map() scala> treasureMap += (1 -> "Go to island.") res1: treasureMap.type = Map(1 -> Go to island.) scala> treasureMap += (2 -> "Find big X on ground.") res2: treasureMap.type = Map(2 -> Find big X on ground., 1 -> Go to island.)
指定したkeyと値を持つMapを作成する場合は↓のようにしたらOKです。
scala> val phone = Map(1 -> "iPhone", 2 -> "Galaxy") phone: scala.collection.immutable.Map[Int,String] = Map(1 -> iPhone, 2 -> Galaxy) scala> phone res0: scala.collection.immutable.Map[Int,String] = Map(1 -> iPhone, 2 -> Galaxy)
ちなみにMapを作成したときにでてきた -> はタプルを生成するためのメソッドで、Mapの要素はkeyとvalueのタプルです。
値を取得するために以下のメソッドを使うことも可能です。
・get
・getOrElse
・apply
//Option型で取得する scala> phone.get(2) res1: Option[String] = Some(Galaxy) //存在しないキーをしていするとNoneを返す scala> phone.get(0) res2: Option[String] = None //keyがない場合の初期値を指定して値を取得する scala> phone.getOrElse(3, "None") res3: String = None //値を取得する scala> phone.apply(1) res1: String = iPhone //存在しないキーを指定するとこのようにNoSuchElementExceptionを返す scala> phone.apply(5) java.util.NoSuchElementException: key not found: 5 at scala.collection.MapLike$class.default(MapLike.scala:228) at scala.collection.AbstractMap.default(Map.scala:59) at scala.collection.MapLike$class.apply(MapLike.scala:141) at scala.collection.AbstractMap.apply(Map.scala:59) ... 32 elided
ちなみにphone.apply(1)とphone(1)は同じ意味で、applyは省略可能です。
Mapの繰り返し処理
scala> val phone = Map( 1 -> "iPhone", 2 -> "Galaxy", 3 -> "Xperia") phone: scala.collection.immutable.Map[Int,String] = Map(1 -> iPhone, 2 -> Galaxy, 3 -> Xperia) scala> phone.foreach { case (key, value) => println(key + ":" + value) } 1:iPhone 2:Galaxy 3:Xperia
//valueを繰り返し処理することも可能です scala> phone.values.foreach { value => println("name is " + value) } name is iPhone name is Galaxy name is Xperia
ここより下で説明している要素の追加や要素の削除は、phoneのMapの構造を直接変更しているわけではありません。
新たなMapを作成してその要素に何かしらの変更を加えています。
Mapに要素を追加する
scala> val phone2 = phone + (4 -> "Nexus") phone2: scala.collection.immutable.Map[Int,String] = Map(1 -> iPhone, 2 -> Galaxy, 3 -> Xperia, 4 -> Nexus)
Mapの要素を削除する
scala> val phone3 = phone - 3 phone3: scala.collection.immutable.Map[Int,String] = Map(1 -> iPhone, 2 -> Galaxy)
その他にもMapで使えるメソッドは↓にあります。
http://www.scala-lang.org/api/2.11.2/#scala.collection.Map
基本的な操作は書きましたが、さらにこんなことできるのかな?というときは上のリンクページを参考にしてみた方が良さそうです。
- 作者: Martin Odersky,Lex Spoon,Bill Venners,羽生田栄一,水島宏太,長尾高弘
- 出版社/メーカー: インプレスジャパン
- 発売日: 2011/09/27
- メディア: 単行本(ソフトカバー)
- 購入: 12人 クリック: 235回
- この商品を含むブログ (45件) を見る
- 作者: 中村修太
- 出版社/メーカー: 中村 修太
- 発売日: 2013/03/02
- メディア: Kindle版
- 購入: 1人 クリック: 27回
- この商品を含むブログ (4件) を見る
scalaのListについて
scalaのリストについて調べてみたのでアウトプットのためにメモしておきます。
Listとは?
Listはscalaでも代表的なimmutableコレクション。
immutableコレクションなので、一度作成すると要素の追加・変更・削除はできません。もしそのような操作をしたい場合は、新しいListコレクションを作成します。
そのため元のコレクションはそのまま存在します。
定義の仕方は?
//NilはListの終わりを示す scala> val nums = 1::2::Nil nums: List[Int] = List(1, 2) scala> val nums = List(1, 2) nums: List[Int] = List(1, 2)
2つともやっていることは同じです。
要素の参照は?
//要素の参照 scala> nums(1) res2: Int = 2 //その指定した要素がない場合 scala> nums(2) java.lang.IndexOutOfBoundsException: 2 at scala.collection.LinearSeqOptimized$class.apply(LinearSeqOptimized.scala:51) at scala.collection.immutable.List.apply(List.scala:83) ... 32 elided
こんなふうにパタンマッチで値をとりだすことも可能です。
scala> nums match { | case x::xs => println(x::xs) | case _ => println("error") | } List(1, 2)
ちなみに下のようにxとxsを表示してみます。
xは先頭の数字で型はInt、xsは先頭を除く値を格納した型はList[Int]であることが分かります。
scala> val list = List(1, 2, 3, 4, 5) list: List[Int] = List(1, 2, 3, 4, 5) scala> list match { | case x::xs => println(x, xs) | case _ => | } (1,List(2, 3, 4, 5))
・Listの要素の取得するには
scala> val smartPhone = List("iPhone", "Xperia", "Galaxy", "Arrows", "Nexus") smartPhone: List[String] = List(iPhone, Xperia, Galaxy, Arrows, Nexus) //要素の最初を取得 scala> smartPhone.head res1: String = iPhone //最初の要素を除くリストを取得 scala> smartPhone.tail res2: List[String] = List(Xperia, Galaxy, Arrows, Nexus) //要素の最後を取得する scala> smartPhone.last res3: String = Nexus //要素の順番を逆にする scala> smartPhone.reverse res4: List[String] = List(Nexus, Arrows, Galaxy, Xperia, iPhone)
・要素の追加は?
immutableなので直接追加はできません。
なので下のように要素を追加したListコレクションを作成することで要素の追加を行います。
//末尾に要素を追加したListを作成 scala> val moreSmartPhone = smartPhone :+ "Kindle Phone" moreSmartPhone: List[String] = List(iPhone, Xperia, Galaxy, Arrows, Nexus, Kindle Phone) //先頭に要素を追加したListを作成 scala> val moreAndMoreSmartPhone = "OnePlus one" :: smartPhone moreAndMoreSmartPhone: List[String] = List(OnePlus one, iPhone, Xperia, Galaxy, Arrows, Nexus)
・要素の削除は?
scala> val lessSmartPhone = smartPhone.filter { _ != "Xperia" } lessSmartPhone: List[String] = List(iPhone, Galaxy, Arrows, Nexus)
複数の要素の削除
scala> val target = List("Arrows", "Galaxy") target: List[String] = List(Arrows, Galaxy) scala> val smartPhone2 = smartPhone.filter { target.contains _ } smartPhone2: List[String] = List(Galaxy, Arrows)
・Listの連結
scala> val ab = List("A", "B") ab: List[String] = List(A, B) scala> val cd = List("C", "D") cd: List[String] = List(C, D) //List同士の連結 scala> val abcd = ab ++ cd abcd: List[String] = List(A, B, C, D) scala> val ef = Array("E", "F") ef: Array[String] = Array(E, F) //ArrayとListの連結も可能 scala> val abef = ab ++ ef abef: List[String] = List(A, B, E, F)
・Listと配列の使い分け
まずは両者の違いです。
List:要素を先頭から順にたどる必要があるため検索が遅い
Array:インデックス指定による特定の要素へのアクセスが高速
使い分け方はといいますと、
先頭の要素に対する操作が多い場合や要素を順に処理する場合はListを使います。
特定のインデックスへのアクセスが多い場合には配列を使うほうがいいでしょう。
本当に色んな使い方ができるので、なにかしたいことがある時にその都度確認してみます。
http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.List
Play2.3でCRUDを使った一覧ページの作成してみる
今回も前回に引き続き、簡単なブログ機能を作成したときに行ったことをまとめていきます。
前回はフォームの内容をDBに反映させる機能をつくりました。今回は一覧表示ページで必要になるDBに入っている内容をとってくる処理をつくっていきます。
playとSquerylでフォームの内容をDBに反映させるまで - hikonori07’s blog
今回使用するpostテーブルやschema定義は↓の記事に書いています。
play frameworkでSqueryl Schema及びentity定義するまで - hikonori07’s blog
今回は主にこんな感じで機能つくっていきます。
・postテーブルから必要な情報を取ってきて一覧表示する
・投稿1つひとつを削除できるようにする
・投稿内容を変更できるようにする
・postテーブルから必要な情報を取ってきて一覧表示する
ここではselectで取ってきたpostテーブルのデータをcontrollerでviewに渡して、viewでそのデータ加工して一覧表示させるということを行います。
//app/controllers/Application.scala def index = Action { transaction { val posts = AppDB.postTable.toList Ok(views.html.index(posts)) } }
↑selectでpostテーブルから必要な情報をとってきて、index.scala.htmlに取ってきた情報を渡しています。
//app/views/index.scala.html @(posts: List[db.Post]) <ul> @for( post <- posts ){ <li>@post.title, @post.content</li> } </ul>
viewでやっていることはほとんど↓のページにまとめられています。
https://www.playframework.com/documentation/2.3.x/ScalaTemplates
一覧の表示はこんな感じでさらっとできました。
・投稿1つひとつを削除できるようにする
ここではview部分で削除できるようにするために必要なroutesの設定、controllerでdeleteアクションの作成などを行います。
//app/views/index.scala.html <ul> @for( post <- posts ){ <li>@post.title, @post.content</li> ■<a href="@routes.Application.deletePost(post.id)">delete</a> } </ul>
idはさっきの一覧を取ってくるselect文では表示されていませんでしたが、↑のように指定することでidを取得することが可能です。
//conf/routes
GET /deletePost/:id @controllers.Application.deletePost(id: Int)
↑getメソッドでdeletePost/:idに遷移した際にApplicationのdeletePostメソッドを呼び出すように設定
//app/controllers/Application.scala def deletePost(id: Int) = Action { inTransaction(AppDB.postTable.deleteWhere(p => p.id === id)) Redirect(routes.Application.index()) }
↑ではviewから受け取ったidをdeleteして、indexページにリダイレクトするという一連の処理をしています。
・投稿内容を変更できるようにする
ここはつまずいたので、詳しく書いていきます。
やることとしては、indexページからupdateページに移動できるようにする。
updateページを開くと現在の該当カラムの内容がフォームに表示されている状態にする。
updateページで入力されたフォームの内容を該当idのカラムにupdateできるようにする。
では下に今回のupdateのための処理をずらっと書いたので、1つずつ見て行きます。
//app/views/update.scala.html @(form: play.api.data.Form[db.Post]) //フォームを宣言して、actionを指定 @helper.form(action = routes.Application.updatePost) { //hiddenに対応したhelperはないため↓のようにHTMLで書く <input type="hidden" name="id" value="@form.data.get("id")" /> @helper.inputText(form("title")) @helper.inputText(form("content")) <input type="submit"/> }
↑updateページの内容です。
その他のhelperの知りたいときは↓
https://www.playframework.com/documentation/2.3.x/ScalaForms
https://github.com/playframework/playframework/tree/master/framework/src/play/src/main/scala/views/helper
//app/views/index.scala.html <ul> @for( post <- posts ){ <li>@post.title, @post.content <p>■<a href="@routes.Application.deletePost(post.id)">delete</a> ■<a href="@routes.Application.update(post.id)">update</a></p> </li> } </ul>
indexページには変更したいpostの下に、編集ページヘのリンクが表示されるようにしました。
//app/conf/routes # update post form //indexページからの遷移先を作成 GET /update/:id @controllers.Application.update(id: Int) //上のupdateページのフォーム送信された際に使うupdatePostメソッドへのroutes設定 POST /updatePost @controllers.Application.updatePost
//変更前 case class Post( title: String, content: String) extends KeyedEntity[Long] { val id: Long = 0 } //変更後 case class Post( id: Long, title: String, content: String) extends KeyedEntity[Long] { }
上が一番つまずいた点だったのですが、変更前の方法だとidを0しか渡してくれません。
insertの場合はidは0で良かったのですが、updateの際にはidは0ではなく投稿と紐付いているidをしっかり渡してくれないと困ります。
なので上のように記述を変更します。
//app/controllers/Application.scala val postForm = Form( mapping( "id" -> ignored(0L), "title" -> text, "content" -> text )(Post.apply)(Post.unapply) ) val updateForm = Form( mapping( "id" -> longNumber, "title" -> text, "content" -> text )(Post.apply)(Post.unapply) ) def update(id: Int) = Action { inTransaction { //該当idに入っている内容を表示させるための処理 from(AppDB.postTable)(p => where(p.id === id) select (p)).headOption.map { post => val filledForm = updateForm.fill(post) Ok(views.html.update(filledForm)) }.getOrElse( BadRequest ) } } //フォームの内容をupdateするための処理 def updatePost = Action { implicit request => updateForm.bindFromRequest.fold( errors => { BadRequest }, post => { inTransaction(AppDB.postTable.update(post)) Redirect(routes.Application.index()) } ) }
postFormがinsert用のフォームです。
updateFormがupdate用のフォームです。
insertの際にはid = 0なので0に固定するためにignored(0L)のような記述をしています。
↓ページのIgnored values参照
https://www.playframework.com/documentation/2.3.x/ScalaForms
updateFormのidはLongなのでlongNumberにします。
How to handle Long and different formats in a Play! Scala form? - Stack Overflow
これでFormはひと通り作成できました。
あとは通常どおりupdateの処理を作成していきます。
update処理で行っているようなことは前のブログ記事でも紹介しましたので省略します。
詳しく知りたい方は、この記事を参考にしてみてください。
playとSquerylでフォームの内容をDBに反映させるまで - hikonori07’s blog
ということで長くなりましたが、これで投稿の削除、更新、一覧表示のされている一覧ページが見れるようになりました。
相手を困らせないための質問のしかた
質問の仕方についてフィードバックされたことを、これから同じことを繰り返さないようにブログに書いておきます。
身についている人にとっては当たり前のことなので、特に読む必要はないかと。
自分のように身についてない人用です。
エンジニアなのでちょこっとだけそれっぽい単語も出てくるかもしれません。
相手にものを聞くために最初に伝えておく事としては、下の5つは用意しておいた方がいいと思う。
・何がしたい?
・何を知っている?
・何を知らない?
・自分がしたことは?
・それに対して起こったことは?
ってことで自分なりに簡単にまとめてみました。
・何がしたい?
これってしたいことを明確にできていないとちゃんと説明できないんですよね。
概念が分かっていない単語ほど、「〜ってどうやるんですか?」みたいな質問をしてしまいます。
なのでその場合は「~がしたいんですが、それをやるためにはどうすれば(手順)いいですか?」のように聞けたらいいのかなと思います。
・何を知っている?
何を知っているか?これを伝えるかどうかで相手の対応は全く変わると思います。そもそもある概念の説明からなのか、したいことをするための方法を教えるべきかなのか。
なので「自分はここまで知っている。その上で〜するためにはどうしたらいいか?」というふうに聞いたらいいかと思います。
・何を知らない?
何を知らないか?そもそも知らないので説明は難しいと思いますが、何を知っているかとセットで「〜までは知っているけど〜からは知らない」と伝えたらいいかと思います。
・自分がしたことは?
自分がしたことを伝える。これをすることで相手に大体の理解度を伝えることができます。
なので何を知っているかより、自分がしたことを伝えた方が早いかもしれません。
わかっているつもりでも、実際にやってみるとできないことって多いですから。
・自分がしたことに対して起こったことは?
これってエンジニアはイメージしやすいことだと思うのですが、それ実行したらどんなエラーコードはいてるの?書いたコードとエラーコードを伝えると大体なにが悪いかが分かります。自分はまだまだ未熟なので分からないことが多いですが。。
これを用意して質問したら大抵大丈夫だと思います。
逆に準備せず聞いてしまうと、なにが知りたいの?とか何回も同じこと言っているのにってことになります。
エンジニアさんは特に同じことを繰り返すことを嫌うので。
ちゃんと気をつけます。
Scalaのapplyとunapplyについて調べてみた
applyとunapplyについて調べてみたのでメモ代わりにブログに書きます。
今回はコップ本の26章抽出子を参考に調べました。
applyメソッド
→使う目的としては値を構築するために使われる
unapplyメソッド
→使う目的としては値をマッチングして分解すること
scala> object Email { | def apply(user:String, domain:String) = user + "@" + domain | def unapply(str: String): Option[(String, String)] = { | val parts = str split "@" | if (parts.length == 2) Some(parts(0), parts(1)) else None | } | } defined object Email //2つの値から1つのメールアドレスを作成 scala> Email( "hikonori", "sample.com") res1: String = hikonori@sample.com //メールアドレスから@の左右を分割して2つの文字列に scala> Email.unapply("hikonori@sample.com") res2: Option[(String, String)] = Some((hikonori,sample.com))
Emailオブジェクトの中では、
applyメソッドを注入(injection)といいます。
→複数の引数をとりそれを基に1組の要素を与えるため
unapplyメソッドは抽出といいます。
→1組の要素を引数としてそれを分解した要素を抽出するため
抽出子オブジェクトについて
抽出子オブジェクトとはメンバーの1つとしてunapplyというメソッドを持っているオブジェクトです。unapplyメソッドの目的はさっきも説明したとおり、値をマッチングして分解することです。
また抽出子オブジェクトは値を構築するための相補的なapplyメソッドを定義していることが多いですが、必須ではありません。
下の例がunapplyメソッドだけでapplyメソッドをもたない抽出子オブジェクトです。
下の抽出子オブジェクトではそもそも値を構築する必要がないため、applyメソッドは必要ありません。
scala> object UpperCase { | def unapply(s: String): Boolean = s.toUpperCase == s | } defined object UpperCase scala> UpperCase.unapply("scala") res1: Boolean = false scala> UpperCase.unapply("SCALA") res2: Boolean = true scala> UpperCase.unapply("Scala") res3: Boolean = false
val userForm = Form( mapping( "name" -> text, "age" -> number )(UserData.apply)(UserData.unapply) )
フォーム定義のとこでよく見る↑の表記は公式サイトではこんな風に説明されていました。
https://www.playframework.com/documentation/2.3.x/ScalaForms
→mapping メソッドには好きな関数を渡すことができます。ケースクラスを構築または分解する場合、デフォルトの apply と unapply 関数がまさしくケースクラスの構築・分解を行う関数なので、それらを渡しておけば問題ありません。
ということで今回はapply、unapplyをみていきました。
これから実際に使うことがあればユースケースなど紹介できればと思います。
追記
unapplyメソッドが定義されている場合のパターンマッチについて
//caseクラスではunapplyが定義される scala> case class Phone( color: String, price: Int) defined class Phone scala> val iPhone6 = Phone( "gold", 650 ) iPhone6: Phone = Phone(gold,650) //unapplyメソッドが定義されていることで値の参照が可能 scala> iPhone6 match{ | case Phone(color, price) => println( "iPhone6は" + color + "で" + price + "です") | case _ => | } iPhone6はgoldで650です >|scala| //通常のクラスではunapplyメソッドは自動で定義されない scala> class Laptop( val color: String, val price: Int) defined class Laptop scala> val macBookPro = new Laptop("silver", 1500) macBookPro: Laptop = Laptop@2f465398 //ケースクラスのように値の参照できない scala> macBookPro match{ | case Laptop( color, price) => println( "MacbookProは" + color + "で" + price + "です") | case _ => | } <console>:11: error: not found: value Laptop case Laptop( color, price) => println( "MacbookProは" + color + "で" + price + "です") ^ //通常のクラスでもコンパニオンオブジェクトでunapplyを定義すると //ケースクラス同様、値の参照ができます scala> class Laptop( val color: String, val price: Int) defined class Laptop scala> object Laptop { | def unapply( l: Laptop):Option[(String, Int)] = Some(( l.color, l.price )) | } defined object Laptop scala> val macBookPro = new Laptop("silver", 1500) macBookPro: Laptop = Laptop@69a3d1d scala> macBookPro match{ | case Laptop( color, price) => println( "MacbookProは" + color + "で" + price + "です") | case _ => | } MacbookProはsilverで1500です
ケースクラスではunapplyメソッドが自動で生成されるので、パターンマッチで値の参照ができました。
しかし通常のクラスではunapplyメソッドは定義されないため、パターンマッチで値の参照を行いたい場合別途unapplyメソッドを定義する必要があります。