Diverse peculiarità di Scala interagiscono per fornire questo comportamento. La prima cosa è che i numeri Manifest
non vengono aggiunti solo all'elenco dei parametri impliciti segreti sul costruttore ma anche sul metodo di copia. E 'ben noto che
case class Foo[+A : Manifest](a: A)
è lo zucchero sintattico per
case class Foo[+A](a: A)(implicit m: Manifest[A])
ma questo riguarda anche il costruttore di copia, che sarebbe simile a questa
def copy[B](a: B = a)(implicit m: Manifest[B]) = Foo[B](a)(m)
Tutti quelli implicit m
s sono creati da th compilatore e inviato al metodo attraverso l'elenco dei parametri impliciti.
Questo andrebbe bene finché si utilizzava il metodo copy
in un punto in cui il compilatore conosceva il parametro di tipo Foo
s. Ad esempio, questo funzionerà al di fuori della classe Bar:
val foo = Foo(1)
val aCopy = foo.copy()
println(aCopy.myManifest) // Prints "Int"
Questo funziona perché il compilatore ne deduce che foo
è un Foo[Int]
quindi sa che foo.a
è un Int
in modo che possa chiamare copy
come questo:
val aCopy = foo.copy()(manifest[Int]())
(si noti che manifest[T]()
è una funzione che crea una rappresentazione manifesta del tipo T
, ad esempio Manifest[T]
con la "M". Non mostrato è l'una ddizione del parametro predefinito in copy
.) Funziona anche all'interno della classe Foo
perché contiene già il manifest passato durante la creazione della classe. Sarebbe simile a questa:
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"
Nell'esempio originale, tuttavia, non riesce nella classe Bar
a causa della seconda peculiarità: mentre i parametri di tipo di Bar
sono conosciuti all'interno della classe Bar
, i parametri di tipo di i parametri del tipo non lo sono. Sa che A
in Bar
è un Foo
o un SubFoo
o SubSubFoo
, ma non se è un Foo[Int]
o un Foo[String]
. Questo è, ovviamente, il ben noto problema di cancellazione di tipo in Scala, ma qui sembra un problema anche quando non sembra che la classe stia facendo qualcosa con il tipo di parametro di tipo foo
s. Ma lo è, ricorda che c'è un'iniezione segreta di un manifest ogni volta che viene chiamato lo copy
e quelli che si manifestano sovrascrivono quelli che c'erano prima.Dal momento che la classe Bar
non ha alcuna idea era il parametro tipo di foo
è, esso crea solo un manifesto di Any
e invia che insieme in questo modo:
def fooCopy = foo.copy()(manifest[Any])
Se uno ha il controllo sulla classe Foo
(ad esempio, non è List
) poi un soluzione che facendo tutto la copia sopra nella classe Foo con l'aggiunta di un metodo che farà la corretta copia, come localCopy
sopra, e restituire il risultato:
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"
Un'altra soluzione è quella di aggiungere Foo
s parametro di tipo come un parametro di tipo manifestata di Bar
:
case class Bar[A <: Foo[B], B : Manifest](foo: A) {
def fooCopy = foo.copy()
}
Ma questo scale male se gerarchia delle classi è di grandi dimensioni, (vale a dire più membri hanno parametri di tipo, e quelle classi hanno anche parametri di tipo) poiché ogni classe dovrebbe avere i parametri di tipo di ogni classe sotto di essa. Sembra anche a fare il tipo di inferenza fuori di testa quando si cerca di costruire un Bar
:
val bar = Bar(Foo(1)) // Does not compile
val bar = Bar[Foo[Int], Int](Foo(1)) // Compiles
Buon lavoro, amico mio! –