generics - method - Escribiendo una función de reparto genérica Scala
scala trait this (3)
Estoy tratando de lograr escribir un método que convierta un valor de Cualquiera a un tipo específico y devuelva la opción en lugar de lanzar una excepción como instanceOf. Scala no se comporta como lo he esperado:
def cast[A](value: Any): Option[A] =
{
try
{
Some(value.asInstanceOf[A])
} catch
{
case e: Exception => None
}
}
La prueba:
val stringOption: Option[String] = cast[String](2)
stringOption must beNone
falla con el error
java.lang.Exception: ''Some(2)'' is not None
Alguien tiene una idea de por qué?
Borrado de lluvias en tu desfile aquí. Por lo tanto, en tiempo de ejecución, el tipo A ya no se conoce y asInstanceOf[A]
se compila en un no-op. Simplemente hace que el compilador crea que el valor resultante es de tipo A, pero que en realidad no está garantizado en el tiempo de ejecución.
Sin embargo, puedes usar los manifiestos de Scala para solucionarlo. Desafortunadamente, el manejo de tipos / boxeo primitivos por parte de la JVM nos obliga a hacer un trabajo extra.
Los siguientes trabajos, aunque no manejan la "conformidad débil" de los tipos, lo que significa que, por ejemplo, un Int no se considera Largo, por lo que cast[Long](42)
devuelve None
.
def cast[A : Manifest](value: Any): Option[A] = {
val erasure = manifest[A] match {
case Manifest.Byte => classOf[java.lang.Byte]
case Manifest.Short => classOf[java.lang.Short]
case Manifest.Char => classOf[java.lang.Character]
case Manifest.Long => classOf[java.lang.Long]
case Manifest.Float => classOf[java.lang.Float]
case Manifest.Double => classOf[java.lang.Double]
case Manifest.Boolean => classOf[java.lang.Boolean]
case Manifest.Int => classOf[java.lang.Integer]
case m => m.erasure
}
if(erasure.isInstance(value)) Some(value.asInstanceOf[A]) else None
}
Esto se debe a la eliminación de tipos. En el tiempo de ejecución, no se conoce A
en la Option[A]
, por lo que se le permite almacenar Some(3)
en una variable de tipo Option[String]
.
La excepción se producirá cuando se acceda al valor dentro de la opción:
scala> val result = cast[String](2)
result: Option[String] = Some(2)
scala> result.get
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at .<init>(<console>:10)
at .<clinit>(<console>)
// ...
Hice prácticamente lo mismo ahora, con Scala 2.10, TypeTags (debido al borrado de tipo) y ValidationNEL de scalaz:
import scala.reflect.runtime.universe._
def as[T: TypeTag](term: Any): ValidationNEL[String, T] =
if (reflect.runtime.currentMirror.reflect(term).symbol.toType <:< typeOf[T])
term.asInstanceOf[T].successNel[String]
else
("Cast error: " + term + " to " + typeOf[T]).failNel[T]
Con la opción en lugar de la validación se vería así:
def as[T: TypeTag](term: Any): Option[T] =
if (reflect.runtime.currentMirror.reflect(term).symbol.toType <:< typeOf[T])
Some(term.asInstanceOf[T])
else
None
Obtuve mi información aquí: ¿Cómo saber si un objeto es una instancia del tipo de TypeTag? , Resolución en tiempo de ejecución de los argumentos de tipo utilizando scala 2.10 reflexión