Tipos abstractos/Parámetros de tipo en Scala
types (1)
Estoy tratando de escribir un código Scala que necesita hacer algo como:
class Test[Type] {
def main {
SomeFunc classOf[Type]
val testVal: Type = new Type()
}
}
y está fallando. Obviamente no entiendo algo sobre los parámetros genéricos de Scala. Claramente, el malentendido es que en C ++, las plantillas funcionan esencialmente como sustituciones de cadenas, por lo que el nuevo Tipo () funcionará siempre que la clase que se pasa tenga un constructor predeterminado. Sin embargo, en Scala, los tipos son diferentes tipos de objetos.
Como usted señala, C ++ tiene plantillas. En resumen, C ++ dice "hay una prueba para todos los tipos T, de manera que la prueba se compila". Eso hace que sea fácil agregar restricciones implícitas en T, pero en el lado negativo están implícitas y pueden ser difíciles de entender para un usuario de su clase sin leer el código.
El polimorfismo paramétrico de Scala (también conocido como genéricos) funciona mucho más como ML, Haskell, Java y C #. En Scala, cuando escribe "Prueba de clase [T]" está diciendo "para todas las T existe un tipo Prueba [T]" sin restricción. Eso es más simple de razonar formalmente, pero significa que tiene que ser explícito sobre las restricciones. Por ejemplo, en Scala puedes decir "Prueba de clase [T <: Foo]" para decir que T debe ser un subtipo de Foo.
C # tiene una manera de agregar una restricción a T con respecto a los constructores, pero desafortunadamente Scala no lo hace.
Hay un par de maneras de resolver su problema en Scala. Uno es tipográfico pero un bt más detallado. El otro no es de tipo seguro.
La forma a prueba de tipos parece
class Test[T](implicit val factory : () => T) {
val testVal = factory
}
Entonces puede tener un cuerpo de fábricas para tipos útiles en su sistema
object Factories {
implicit def listfact[X]() = List[X]()
implicit def setfact[X]() = Set[X]()
// etc
}
import Factories._
val t = new Test[Set[String]]
Si los usuarios de su biblioteca necesitan sus propias fábricas, pueden agregar su propio equivalente del objeto Fábricas. Una ventaja de esta solución es que se puede usar cualquier cosa con una fábrica, ya sea que haya o no un constructor sin argumentos.
La forma no tan segura para los tipos de uso utiliza la reflexión y una característica en Scala llamada manifiestos que son una forma de sortear una restricción de Java relacionada con el borrado de tipos
class Test[T](implicit m : Manifest[T]) {
val testVal = m.erasure.newInstance().asInstanceOf[T]
}
Con esta versión sigues escribiendo
class Foo
val t = new Test[Foo]
Sin embargo, si no hay un constructor sin argumentos disponible, se obtiene una excepción de tiempo de ejecución en lugar de un error de tipo estático
scala> new Test[Set[String]]
java.lang.InstantiationException: scala.collection.immutable.Set
at java.lang.Class.newInstance0(Class.java:340)