Scalaでthis.typeとcloneを組み合わせたときに起こる問題について、@cocoa_rutoさんの一連のポストが興味深かったので引用してみる:
Scalaでthis.typeなフィールドを持つオブジェクトをcloneしてもそのフィールドは元のオブジェクトを差す。クラスの型メンバも Outer.this.type#Innerという型を持つのでInnerをフィールドとして持つOuterをcloneすると悲しいことになる。
http://twitter.com/cocoa_ruto/status/6399352940
cloneした後で newOuter.inner = new newOuter.Innerとやる。Innerのサブクラスを作っている場合は、innerの型をOuter#Innerと明示して、Innerに create(o: Outer): Outer#Innerを用意する。
http://twitter.com/cocoa_ruto/status/6399447696
ちなみにJavaでいうところのouter.new Inner()はScalaではnew outer.Innerと書きます。
http://twitter.com/cocoa_ruto/status/6399637997
Scalaの型はJavaの型よりも強いので、Javaのコードを呼び出すと型安全性でなくなるということか。
http://twitter.com/cocoa_ruto/status/6399802258
もうちょっと詳しく言うと、不正なバイトコードを吐くわけじゃない(多分)ので弱健全性は成り立つ(本当は意味論がちゃんと決まらないとなんとも言えないんだけど)。一方でScalaの this.type型はthisとnullのみを持つ集合と定義されてるので、強健全性は成り立たない。
http://twitter.com/cocoa_ruto/status/6401820138
なるほどなるほど。もうちょっと単純な例で言うと、
class X extends Cloneable { var myself: this.type = this override def clone: X = super.clone.asInstanceOf[X] } val x1 = new X val x2 = x1.clone
があったときに、
x1.myself eq x1
は成り立つけど、
x2.myself eq x2
と
x2.mysefl eq null
は成り立たない、という話ですね(x2.myselfの型はthis.typeである、つまり、x2自身かnullのどちらかでなければならない事が型システムで定められているのにそれが破られている)。ただ、より正確に言うなら、これは、Javaのメソッドを呼び出しているから、というよりもシャローコピーとthis.type(おそらくもっと一般化してsingleton typeに関しても)の相性が悪いというべきではないでしょうか。
というのは、このケースについて、AnyRef#clone()の処理(=シャローコピー)をScalaレベルのコードで書くと、
val x1 = new X val x2 = new X x2.myself = x1.myself
のようになりますが(厳密には違いますが、このケースではそう考えて問題無いはず)、このようなコードは実際にScalaで書くと型チェッカーによってrejectされます。しかし、cloneメソッドが内部で行っている処理はScalaの型チェッカーによるチェックをスキップするため、上記の処理に相当する型安全でない処理がチェックされないまま実行が行われ、結果として型安全ではなくなるわけです。