とりあえず、以下のNew featuresから順に興味のあるものを試していってみようと思います。
http://www.scala-lang.org/node/27499
まあ、基本他の方々のおっかけですよね…。
まずはValue Classes( and Universal Traits)から。
Value Classes
ドキュメント
http://docs.scala-lang.org/overviews/core/value-classes.html
素晴らしいことに、もう日本語訳が!
http://docs.scala-lang.org/ja/overviews/core/value-classes.html
だいたいこれを読めばわかるのですが、要は
- AnyValのサブクラスを作成できる
- publicなvalをひとつだけ持てる
- (コンパイル時にインライン展開されるので、)実行時のオブジェクトの割り当てを回避できる
というものだそうで。
制約とかも載っているのであんまり書くのもあれですが、以下のような場合にインスタンスが生成されるんだよってことは注意した方がいいのかな?
- 値クラスが別の型として扱われるとき
- 値クラスが配列に代入されるとき
- パターンマッチングなどにおいて、実行時の型検査を行うとき
とりあえず、サンプルを。
ValueClasses.scala
class IntWrapper(val underlying: Int) extends AnyVal { def show(): Unit = println(s"This Value = [$underlying]") } object ValueClasses { def main(args: Array[String]): Unit = { val iw = new IntWrapper(10) iw.show() } }
IntWrapperがValue Classです。
これをコンパイルして実行すると
$ scalac ValueClasses.scala $ scala ValueClasses This Value = [10]
となります。
この時のコンパイル途中の結果をちょっと覗いてみます。「-Xprint:posterasure」が、インライン展開を終えた後になるようです。
$ scalac -Xprint:posterasure ValueClasses.scala [[syntax trees at end of posterasure]] // ValueClasses.scala package <empty> { final class IntWrapper extends Object { 〜省略〜 object ValueClasses extends Object { def <init>(): ValueClasses.type = { ValueClasses.super.<init>(); () }; def main(args: Array[String]): Unit = { val iw: Int = 10; IntWrapper.this.show$extension(iw) } } }
なるほど、確かにインライン展開されてますね。
def main(args: Array[String]): Unit = { val iw: Int = 10; IntWrapper.this.show$extension(iw) }
Universal Traits(汎用トレイト)
何か見慣れないキーワードなのですが、以下のもののようです。
Value ClassにトレイトをMix-inすることができるようなのですが、それは汎用トレイトに限られますよと。
まんまですが、こんなやつです。
trait Printable extends Any { def print(): Unit = println(this) }
こんなトレイトであれば、Value ClassにMix-inできますよ、と。
class IntWrapper(val underlying: Int) extends AnyVal with Printable { def show(): Unit = println(s"This Value = [$underlying]") }
こんな感じにトレイトから「extends Any」を外すと
trait Printable { def print(): Unit = println(this) } class IntWrapper(val underlying: Int) extends AnyVal with Printable { def show(): Unit = println(s"This Value = [$underlying]") }
以下のように「継承関係がおかしいよ!AnyValはObjectのサブクラスじゃないぞ!」と怒られます。
ValueClasses.scala:5: error: illegal inheritance; superclass AnyVal is not a subclass of the superclass Object of the mixin trait Printable class IntWrapper(val underlying: Int) extends AnyVal with Printable { ^ one error found
で、今度はこんな感じに修正して
ValueClasses.scala
trait Printable extends Any { def print(): Unit = println(this) } class IntWrapper(val underlying: Int) extends AnyVal with Printable { def show(): Unit = println(s"This Value = [$underlying]") } object ValueClasses { def main(args: Array[String]): Unit = { val iw = new IntWrapper(10) iw.show() iw.print() // <- ここ、追加 } }
$ scalac -Xprint:posterasure ValueClasses.scala [[syntax trees at end of posterasure]] // ValueClasses.scala package <empty> { abstract trait Printable extends Object { def print(): Unit }; final class IntWrapper extends Object with Printable { 〜省略〜 object ValueClasses extends Object { def <init>(): ValueClasses.type = { ValueClasses.super.<init>(); () }; def main(args: Array[String]): Unit = { val iw: Int = 10; IntWrapper.this.show$extension(iw); new IntWrapper(iw).print() } }; abstract trait Printable$class extends Object with Printable { def /*Printable$class*/$init$(): Unit = { () }; def print(): Unit = scala.this.Predef.println(Printable$class.this) } }
確かにトレイトのメソッドを使用する時に、インスタンスを作成するようになっていますね。
def main(args: Array[String]): Unit = { val iw: Int = 10; IntWrapper.this.show$extension(iw); new IntWrapper(iw).print() }
スクリプトでは使えない?
Value Classの制限のひとつに、
- トップレベルクラスか静的にアクセス可能なオブジェクトのメンバである必要がある
というものがあり、
object Outer { class Inner(val x: Int) extends AnyVal }
というような表記はOKだよ(objectのメンバはstaticになるので)、というのがあります。
が、scalaコマンドで直接実行するような形式の場合はValue Classの利用は不可のようです。
例えば、以下のようなスクリプトを用意して
value_classes_script.scala
class IntWrapper(val underlying: Int) extends AnyVal object Outer { class Inner(val x: Int) extends AnyVal }
scalaコマンドで実行すると、エラーになります。
$ scala value_classes_script.scala /xxxxx/value_classes_script.scala:1: error: value class may not be a member of another class class IntWrapper(val underlying: Int) extends AnyVal ^ /xxxxx/value_classes_script.scala:4: error: value class may not be a member of another class class Inner(val x: Int) extends AnyVal ^ two errors found
スクリプト形式の場合は、Mainというオブジェクトの中のmainメソッドの中に定義されることになるようなので、そりゃあそうだよね…という気もしますが。まあ、スクリプト形式でこの機能が使えなくて困る局面があるか?という話ですね。