いくつかの Scala の特性が相互作用して、この動作を実現します。まず、Manifest
s がコンストラクターの秘密の暗黙パラメーター リストに追加されるだけでなく、copy メソッドにも追加されることです。ことはよく知られている.
case class Foo[+A : Manifest](a: A)
は単なるシンタックスシュガーです
case class Foo[+A](a: A)(implicit m: Manifest[A])
ただし、これはコピー コンストラクターにも影響します。これは次のようになります。
def copy[B](a: B = a)(implicit m: Manifest[B]) = Foo[B](a)(m)
これらすべてのimplicit m
s はコンパイラによって作成され、暗黙的なパラメーター リストを介してメソッドに送信されます。
コンパイラが型パラメータcopy
を知っている場所でメソッドを使用している限り、これは問題ありません。Foo
たとえば、これは Bar クラスの外で機能します。
val foo = Foo(1)
val aCopy = foo.copy()
println(aCopy.myManifest) // Prints "Int"
これが機能するのは、コンパイラがそれfoo
が であると推測し、それが であるFoo[Int]
ことがわかっているfoo.a
ためInt
ですcopy
。
val aCopy = foo.copy()(manifest[Int]())
manifest[T]()
(は、型のマニフェスト表現を作成する関数であることに注意してください。T
たとえばManifest[T]
、大文字の「M」を使用します。デフォルト パラメータの への追加は示されていません。)渡されたマニフェストが既にあるため、クラスcopy
内でも機能します。Foo
クラスが作成されたとき。次のようになります。
case class Foo[+A : Manifest](a: A) {
def myManifest = implicitly[Manifest[_ <: A]]
def localCopy = copy()
}
val foo = Foo(1)
println(foo.localCopy.myManifest) // Prints "Int"
ただし、元の例ではBar
、2 番目の特殊性のためにクラスで失敗します。クラスBar
内での型パラメーターは認識されBar
ていますが、型パラメーターの型パラメーターは認識されていません。A
inBar
が aFoo
または aSubFoo
またはであることは認識していますが、 aまたはa である場合は認識してSubSubFoo
いません。もちろん、これは Scala でよく知られている型消去の問題ですが、クラスがs 型パラメーターの型で何もしていないように見える場合でも、ここでは問題として現れます。ただし、呼び出されるたびにマニフェストの秘密の注入があり、それらのマニフェストは以前にあったものを上書きすることを覚えておいてください。クラスにはわからないので、の型パラメーターでしたFoo[Int]
Foo[String]
foo
copy
Bar
foo
つまり、次のようにマニフェストを作成してAny
送信するだけです。
def fooCopy = foo.copy()(manifest[Any])
クラスを制御できる場合Foo
(たとえば、 ではない場合List
)、上記のように適切なコピーを実行しlocalCopy
、結果を返すメソッドを追加して、Foo クラスですべてのコピーを実行することにより、1 つの回避策を実行します。
case class Bar[A <: Foo[Any]](foo: A) {
//def fooCopy = foo.copy()
def fooCopy = foo.localCopy
}
val bar = Bar(Foo(1))
println(bar.fooCopy.myManifest) // Prints "Int"
Foo
別の解決策は、 s 型パラメーターを のマニフェスト型パラメーターとして追加することBar
です。
case class Bar[A <: Foo[B], B : Manifest](foo: A) {
def fooCopy = foo.copy()
}
ただし、クラス階層が大きい場合 (つまり、より多くのメンバーが型パラメーターを持ち、それらのクラスにも型パラメーターがある場合)、すべてのクラスがその下のすべてのクラスの型パラメーターを持つ必要があるため、これはうまくスケーリングしません。また、次のように構築しようとすると、型推論が狂ってしまうようBar
です。
val bar = Bar(Foo(1)) // Does not compile
val bar = Bar[Foo[Int], Int](Foo(1)) // Compiles