Es un hecho triste de la vida en Scala que si crea una instancia de una Lista [Int], puede verificar que su instancia sea una Lista, y puede verificar que cualquier elemento individual de la misma sea una Int, pero no que sea una Lista [ Int], como se puede verificar fácilmente:
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!
La opción sin marcar pone la culpa directamente en el tipo de borrado:
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
case l : List[String] => println("A list of strings?!")
^
A list of strings?!
¿Por qué es eso y cómo puedo evitarlo?
scala
type-erasure
Daniel C. Sobral
fuente
fuente
TypeTag
s .scala 2.10.2
, vi esta advertencia:<console>:9: warning: fruitless type test: a value of type List[Int] cannot also be a List[String] (but still might match its erasure) case list: List[String] => println("a list of strings?") ^
creo que su pregunta y respuesta son muy útiles, pero no estoy seguro de si esta advertencia actualizada es útil para los lectores.Respuestas:
Scala se definió con Type Erasure porque la máquina virtual Java (JVM), a diferencia de Java, no obtuvo genéricos. Esto significa que, en tiempo de ejecución, solo existe la clase, no sus parámetros de tipo. En el ejemplo, JVM sabe que está manejando un
scala.collection.immutable.List
, pero no que esta lista esté parametrizada conInt
.Afortunadamente, hay una característica en Scala que te permite evitar eso. Es el manifiesto . Un manifiesto es una clase cuyas instancias son objetos que representan tipos. Dado que estas instancias son objetos, puede pasarlas, almacenarlas y, por lo general, invocar métodos en ellas. Con el soporte de parámetros implícitos, se convierte en una herramienta muy poderosa. Tome el siguiente ejemplo, por ejemplo:
Al almacenar un elemento, también almacenamos un "Manifiesto". Un manifiesto es una clase cuyas instancias representan tipos de Scala. Estos objetos tienen más información que JVM, lo que nos permite probar el tipo completo y parametrizado.
Sin embargo,
Manifest
tenga en cuenta que a sigue siendo una característica en evolución. Como ejemplo de sus limitaciones, actualmente no sabe nada sobre la varianza, y supone que todo es covariante. Espero que se vuelva más estable y sólido una vez que la biblioteca de reflexión Scala, actualmente en desarrollo, se termine.fuente
get
método se puede definir comofor ((om, v) <- _map get key if om <:< m) yield v.asInstanceOf[T]
.TypeTag
realidad se usan automáticamente en la coincidencia de patrones? Genial, ¿eh?Manifest
parámetro en sí mismo, consulte: stackoverflow.com/a/11495793/694469 "la instancia [manifest / type-tag] [...] está siendo creada implícitamente por el compilador "Puede hacer esto usando TypeTags (como Daniel ya menciona, pero lo explicaré explícitamente):
También puede hacer esto usando ClassTags (que le ahorra tener que depender de scala-reflect):
Las etiquetas de clase se pueden usar siempre que no espere que el parámetro de tipo
A
sea un tipo genérico.Desafortunadamente, es un poco detallado y necesita la anotación @unchecked para suprimir una advertencia del compilador. El Compilador puede incorporar automáticamente la TypeTag en la coincidencia de patrones en el futuro: https://issues.scala-lang.org/browse/SI-6517
fuente
[List String @unchecked]
, ya que no añade nada a esta coincidencia de patrones (sólo usarcase strlist if typeOf[A] =:= typeOf[String] =>
lo hará, o inclusocase _ if typeOf[A] =:= typeOf[String] =>
si no se necesita la variable ligada en el cuerpo delcase
).=>
se ejecuta el código a la derecha del . (Y cuando se ejecuta el código en la HR, los guardias brindan una garantía estática sobre el tipo de elementos. Puede haber un elenco allí, pero es seguro.)Puede usar la
Typeable
clase de tipo de shapeless para obtener el resultado que busca,Ejemplo de sesión REPL,
La
cast
operación será tan precisa como sea posible debido a lasTypeable
instancias disponibles dentro del alcance .fuente
l1.cast[List[String]]
aproximadamentefor (x<-l1) assert(x.isInstanceOf[String]
) Para grandes estructuras de datos o si los moldes ocurren con mucha frecuencia, esto puede ser una sobrecarga inaceptable.Se me ocurrió una solución relativamente simple que sería suficiente en situaciones de uso limitado, esencialmente envolviendo tipos parametrizados que sufrirían el problema de borrado de tipos en las clases de envoltura que se pueden usar en una declaración de coincidencia.
Esto tiene el resultado esperado y limita el contenido de nuestra clase de caso al tipo deseado, Listas de cadenas.
Más detalles aquí: http://www.scalafied.com/?p=60
fuente
Hay una manera de superar el problema de borrado de tipo en Scala. En Overcoming Type Erasure en la coincidencia 1 y Overcoming Type Erasure en Matching 2 (Variance) hay alguna explicación de cómo codificar algunos ayudantes para ajustar los tipos, incluida la Variance, para la coincidencia.
fuente
Encontré una solución ligeramente mejor para esta limitación del lenguaje que de otra manera sería increíble.
En Scala, el problema del borrado de tipo no ocurre con las matrices. Creo que es más fácil demostrar esto con un ejemplo.
Digamos que tenemos una lista de
(Int, String)
, luego lo siguiente da un tipo de advertencia de borradoPara evitar esto, primero cree una clase de caso:
luego, en la coincidencia de patrones, haga algo como:
que parece funcionar perfectamente
Esto requerirá cambios menores en su código para trabajar con matrices en lugar de listas, pero no debería ser un problema importante.
Tenga en cuenta que el uso
case a:Array[(Int, String)]
seguirá dando una advertencia de borrado de tipo, por lo que es necesario utilizar una nueva clase de contenedor (en este ejemploIntString
).fuente
Como Java no conoce el tipo de elemento real, me pareció más útil usarlo
List[_]
. Luego, la advertencia desaparece y el código describe la realidad: es una lista de algo desconocido.fuente
Me pregunto si esta es una solución alternativa adecuada:
¡No coincide con el caso de "lista vacía", pero da un error de compilación, no una advertencia!
Esto por otro lado parece funcionar ...
¿No es aún mejor o me estoy perdiendo el punto aquí?
fuente
No es una solución, sino una forma de vivir con ella sin barrerla por completo debajo de la alfombra: agregar la
@unchecked
anotación. Ver aquí: http://www.scala-lang.org/api/current/index.html#scala.uncheckedfuente
Quería agregar una respuesta que generalice el problema a: ¿Cómo puedo obtener una representación de cadena del tipo de mi lista en tiempo de ejecución?
fuente
Usando guardia de coincidencia de patrones
fuente
isInstanceOf
realiza una verificación de tiempo de ejecución en función de la información de tipo disponible para la JVM. Y esa información de tiempo de ejecución no contendrá el argumento de tipo aList
(debido a la eliminación de tipo).