2011-08-14 10 views
17

Estoy tratando de lograr escribir un método que arroje un valor de Cualquiera a un tipo específico y devuelve la opción en lugar de arrojar una excepción como instanceOf. Scala no se comporta como si hubiera esperado:Escribiendo una función de conversión genérica Scala

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 

produce el error

java.lang.Exception: 'Some(2)' is not None 

Alguien tiene una idea de por qué?

+0

Lanzar un valor entero a una cadena debe conducir a una excepción y el método debe devolver None, pero no es el caso. Yo uso scala 2.9.0-1 –

+0

Sí, devuelve Some (2) pero ... not. Tratar de 'obtener' el valor arroja la excepción, pero 'getOrElse' está bien. –

+0

Sí exactamente, esperaba que ocurriera una excepción en el método de conversión. –

Respuesta

21

lluvias Erasure en su desfile aquí. Por lo tanto, en el tiempo de ejecución, el tipo A ya no se conoce y asInstanceOf[A] se compila en una operación no operativa. 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, puede usar los manifiestos de Scala para solucionar el problema. Lamentablemente, el manejo de tipos primitivos/boxeo de la JVM nos obliga a hacer un trabajo extra.

Lo siguiente funciona, aunque no maneja la "conformidad débil" de los tipos, lo que significa que, p. un Int no se considera un 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 
} 
+0

no es verdad - asInstanceOf siempre compila en operaciones de verificación para tipos no primitivos y genéricos – dk14

5

Esto se debe a borrado de tipo. En tiempo de ejecución, se desconoce A en Option[A], por lo que puede almacenar Some(3) en una variable del tipo Option[String].

La excepción se produce cuando se accede 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>) 
     // ... 
+0

¿Por qué 'getOrElse (42)' no genera esta excepción pero evalúa a 2? –

+2

El tipo de devolución de 'Opción [A] .getOrElse [B]' debe ser un supertipo de 'A' y' B'. En el caso de 'String' y' Int', el tipo de devolución es 'Any' y, por supuesto, puede convertir' 2' en 'Any'. Si intentaste 'getOrElse (" 42 ")' obtendrías 'ClassCastException', ya que el tipo de retorno sería' String'. –

+0

Tiene sentido, por lo que no hay una manera fácil de escribir un método como ese de una manera genérica? –

2

lo hice más o menos lo mismo que ahora, con la Scala 2.10, TypeTags (debido al tipo de borrado) 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 opción en lugar de validación se vería como este :

def as[T: TypeTag](term: Any): Option[T] = 
    if (reflect.runtime.currentMirror.reflect(term).symbol.toType <:< typeOf[T]) 
    Some(term.asInstanceOf[T]) 
    else 
    None 

tengo mis informaciones aquí: How to know if an object is an instance of a TypeTag's type?, Runtime resolution of type arguments using scala 2.10 reflection

Cuestiones relacionadas