Tipi astratti / Parametri di tipo in Scala
Domanda
Sto cercando di scrivere del codice Scala che deve fare qualcosa del tipo:
class Test[Type] {
def main {
SomeFunc classOf[Type]
val testVal: Type = new Type()
}
}
e non riesce. Ovviamente non capisco qualcosa sui parametri generici di Scala. Chiaramente, il malinteso è che in C ++ i template funzionano essenzialmente come sostituzioni di stringhe, quindi new Type () funzionerà fintanto che la classe che viene passata ha un costruttore predefinito. Tuttavia, in Scala, i tipi sono diversi tipi di oggetti.
Soluzione
Come fai notare, C ++ ha dei template. In breve, C ++ dice & Quot; esiste un Test per tutti i tipi T in modo che Test compili. & Quot; Ciò rende facile aggiungere implicitamente vincoli su T, ma sul lato negativo sono impliciti e possono essere difficili da comprendere per un utente della tua classe senza leggere il codice.
Il polimorfismo parametrico di Scala (alias generici) funziona molto di più come ML, Haskell, Java e C #. In Scala, quando scrivi & Quot; class Test [T] & Quot; stai dicendo " per tutte le T esiste un tipo Test [T] " senza vincolo. È più semplice ragionare formalmente, ma significa che devi essere esplicito sui vincoli. Ad esempio, in Scala puoi dire & Quot; class Test [T & Lt ;: Foo] & Quot; per dire che T deve essere un sottotipo di Foo.
C # ha un modo per aggiungere un vincolo a T per quanto riguarda i costruttori, ma sfortunatamente Scala no.
Ci sono un paio di modi per risolvere il tuo problema in Scala. Uno è typesafe ma un bt più dettagliato. L'altro non è dattiloscritto.
Il tipo di dattiloscritto appare come
class Test[T](implicit val factory : () => T) {
val testVal = factory
}
Quindi puoi avere una serie di fabbriche per tipi utili nel tuo sistema
object Factories {
implicit def listfact[X]() = List[X]()
implicit def setfact[X]() = Set[X]()
// etc
}
import Factories._
val t = new Test[Set[String]]
Se gli utenti della tua biblioteca hanno bisogno delle proprie fabbriche, possono aggiungere il loro equivalente dell'oggetto Fabbriche. Un vantaggio di questa soluzione è che qualsiasi cosa con una fabbrica può essere utilizzata, indipendentemente dal fatto che esista o meno un costruttore no-arg.
Il modo non tipicamente tipografico usa la riflessione e una caratteristica in Scala chiamata manifest, che è un modo per aggirare un vincolo Java relativo alla cancellazione del tipo
class Test[T](implicit m : Manifest[T]) {
val testVal = m.erasure.newInstance().asInstanceOf[T]
}
Con questa versione scrivi ancora
class Foo
val t = new Test[Foo]
Tuttavia, se non è disponibile alcun costruttore no-arg, si ottiene un'eccezione di runtime invece di un errore di tipo statico
scala> new Test[Set[String]]
java.lang.InstantiationException: scala.collection.immutable.Set
at java.lang.Class.newInstance0(Class.java:340)