Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

ScalaのAdvanced Exampleを写経する(7)-custom 'apply/update'

Scalaでは値を取り出すときはapplyメソッドが、値を設定するときはupdateメソッドが呼び出されるらしい。
それらに任意の処理を設定するサンプル。
properties.scala | The Scala Programming Language

package sample.snippet

/** Illustrate the use of custom 'apply/update' methods. */
object properties {
  
  /** A mutable propert whose getter and setter may be customized. */
  case class Property[T](init: T) {
    private var value: T = init
    
    private var setter: T => T = identity[T]
    
    private var getter: T => T = identity[T]
    
    /** Retrieve the value held in this property. */
    def apply(): T = getter(value)
    
    /** Update the value held in this property, through the setter. */
    def update(newValue: T) = value = setter(newValue)
    
    /** Change the getter. */
    def get(newGetter: T => T) = { getter = newGetter; this }
    
    /** Change the setter. */
    def set(newSetter: T => T) = { setter = newSetter; this }
  }
  
  class User {
    // Create a property with custom getter and setter
    val firstname = Property("")
    .get{ v => v.toUpperCase() }
    .set { v => "Mr. " + v }
    
    val lastname = Property("<noname>")
    
    /** Scala provides syntactic sugar for calling 'apply'.
     * Simply adding a list of arguments between parenthesis
     * (int this case, an empty list) is translated to a call
     * to 'apply' woth those argyments.
     */
    override def toString() = firstname() + " " + lastname()
  }
  
  def main(args: Array[String]) {
    val user1 = new User
    // Syntactic sugar for 'update': an assignment is translated to a call to method 'update'
    user1.firstname() = "Robert"
    
    val user2 = new User
    user2.firstname() = "bob"
    user2.lastname() = "KUZ"
    
    println("user1: " + user1)
    println("user2: " + user2)    
  }
}

Propertyクラスから見ていくと、valueが実際の値。
setterもgetterもT型の引数をもらいT型の値を返す関数。
デフォルトはPredefクラスに定義されてるidentityメソッド。

  implicit def identity[A](x: A): A = x

そのままxを返すだけ。何の処理もしてない。


で、applyはgetterを呼び出し、updateはsetterを呼び出す。
get,setでgetter,setterの関数を変更する。


Userクラスでは、firstnameはgetすると大文字で返す関数。setは文頭にMr.をつけた値を返す関数。
setで値を返すけど、updateメソッドを見るとわかるように、setter関数の戻り値をvalueに代入するからね。


toStringメソッドをオーバーライドしてるけど、要は変数名のうしろに()をつけるとapply/updateが呼び出されますよーということ。
そういうシンタックスシュガーだって。
引数があれば()の中に書けばいいと。


ん?じゃあこれを

user2.firstname() = "bob"

こう書いてもいいってこと?

user2.firstname("bob") 

コンパイルエラー。。。
「wrong number of arguments for method apply」。
値を更新してるからupdateメソッドじゃないの??
コメントには「Syntactic sugar for 'update': an assignment is translated to a call to method 'update'」ってあるし。
でも、firstname("bob")のようなシンタックスシュガーはないってことかな。


実行結果。

user1: MR. ROBERT <noname>
user2: MR. BOB KUZ