Kilka osobliwości Scala wchodzi w interakcję, aby dać to zachowanie. Po pierwsze, są one dołączane nie tylko do tajnej listy niejawnych parametrów konstruktora, ale także do metody kopiowania. Powszechnie wiadomo, że
case class Foo[+A : Manifest](a: A)
jest cukier tylko syntaktyczny dla
case class Foo[+A](a: A)(implicit m: Manifest[A])
ale również wpływa konstruktor kopiujący, który wyglądałby następująco
def copy[B](a: B = a)(implicit m: Manifest[B]) = Foo[B](a)(m)
Wszystkie te implicit m
s są tworzone przez th e kompilator i wysłany do metody poprzez niejawną listę parametrów.
To byłoby w porządku tak długo, jak długo używano metody copy
w miejscu, w którym kompilator znał parametr typu: . Na przykład, to będzie działać na zewnątrz klasy Bar:
val foo = Foo(1)
val aCopy = foo.copy()
println(aCopy.myManifest) // Prints "Int"
To działa, ponieważ kompilator wnioskuje że foo
jest Foo[Int]
więc wie, że foo.a
jest Int
więc można go nazwać copy
tak:
val aCopy = foo.copy()(manifest[Int]())
(Zauważ, że manifest[T]()
to funkcja, która tworzy oczywisty reprezentacji typu T
, np Manifest[T]
przez duże „M”. Nie pokazano Czy a ddycja domyślnego parametru do copy
.) Działa również w klasie Foo
, ponieważ ma już manifest, który został przekazany podczas tworzenia klasy. To mniej więcej tak wyglądać:
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"
W oryginalnym przykład, jednak nie w klasie Bar
bo drugiego osobliwość: podczas gdy parametry Rodzaj Bar
są znane w klasie Bar
, parametry Rodzaj parametry typu nie są. Wie, że A
w Bar
jest lub SubSubFoo
, ale nie, jeśli jest to Foo[Int]
lub. Jest to oczywiście dobrze znany problem usuwania czcionek w Scali, ale pojawia się tutaj problem, nawet jeśli nie wydaje się, aby klasa wykonywała cokolwiek z typem parametru typu: foo
. Ale tak jest, pamiętaj, że istnieje tajny zastrzyk manifestu za każdym razem, gdy jest wywoływany, a te manifesty zastępują te, które były tam wcześniej.Ponieważ klasa Bar
nie ma pomysł był parametr typ foo
jest, po prostu tworzy manifest Any
i wysyła że wzdłuż tak:
def fooCopy = foo.copy()(manifest[Any])
Jeśli ktoś ma kontrolę nad klasą Foo
(np nie jest List
) następnie jeden obejście go wykonując wszystkie kopiowanie w klasie Foo dodając metodę, która zrobi właściwą kopiowanie, jak localCopy
powyżej, i zwraca wynik:
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"
Innym rozwiązaniem jest dodanie Foo
s typ parametru jako przejawionego parametru typu Bar
:
case class Bar[A <: Foo[B], B : Manifest](foo: A) {
def fooCopy = foo.copy()
}
Ale to źle, jeśli klasa Wagi hierarchia jest duża (to znaczy więcej członków ma parametry typu, a te klasy również mają parametry typu), ponieważ każda klasa musiałaby mieć parametry typu każdej klasy poniżej. Wydaje się również, aby rodzaj wnioskowania freak out gdy próbuje skonstruować Bar
:
val bar = Bar(Foo(1)) // Does not compile
val bar = Bar[Foo[Int], Int](Foo(1)) // Compiles
Dobra robota, przyjacielu! –