C'est une triste réalité de Scala que si vous instanciez une liste [Int], vous pouvez vérifier que votre instance est une liste, et vous pouvez vérifier que tout élément individuel de celle-ci est un int, mais pas qu'il s'agit d'une liste [ Int], comme cela peut être facilement vérifié:
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?!
L'option -unchecked met le blâme carrément sur l'effacement de type:
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?!
Pourquoi cela, et comment puis-je contourner ce problème?
scala
type-erasure
Daniel C. Sobral
la source
la source
TypeTag
s .scala 2.10.2
, j'ai vu cet avertissement à la place:<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?") ^
je trouve que votre question et votre réponse sont très utiles, mais je ne sais pas si cet avertissement mis à jour est utile aux lecteurs.Réponses:
Scala a été défini avec Type Erasure car la machine virtuelle Java (JVM), contrairement à Java, n'a pas reçu de génériques. Cela signifie qu'au moment de l'exécution, seule la classe existe, pas ses paramètres de type. Dans l'exemple, la JVM sait qu'elle gère un
scala.collection.immutable.List
, mais pas que cette liste est paramétrée avecInt
.Heureusement, il existe une fonctionnalité dans Scala qui vous permet de contourner cela. C'est le manifeste . Un manifeste est une classe dont les instances sont des objets représentant des types. Étant donné que ces instances sont des objets, vous pouvez les transmettre, les stocker et généralement y appeler des méthodes. Avec la prise en charge de paramètres implicites, il devient un outil très puissant. Prenons l'exemple suivant, par exemple:
Lorsque nous stockons un élément, nous en stockons également un "manifeste". Un manifeste est une classe dont les instances représentent des types Scala. Ces objets ont plus d'informations que la JVM, ce qui nous permet de tester le type complet et paramétré.
Notez, cependant, que a
Manifest
est toujours une fonction évolutive. À titre d'exemple de ses limites, il ne connaît actuellement rien de la variance et suppose que tout est co-variant. Je m'attends à ce qu'il devienne plus stable et solide une fois la bibliothèque de réflexion Scala, actuellement en cours de développement, terminée.la source
get
méthode peut être définie commefor ((om, v) <- _map get key if om <:< m) yield v.asInstanceOf[T]
.TypeTag
sont en fait automatiquement utilisés pour la correspondance de motifs? Cool, hein?Manifest
paramètre lui-même, voir: stackoverflow.com/a/11495793/694469 "l'instance [manifest / type-tag] [...] est créée implicitement par le compilateur "Vous pouvez le faire en utilisant TypeTags (comme Daniel le mentionne déjà, mais je vais simplement l'expliquer explicitement):
Vous pouvez également le faire à l'aide de ClassTags (ce qui vous évite d'avoir à dépendre de scala-reflect):
Les ClassTags peuvent être utilisés tant que vous ne vous attendez pas à ce que le paramètre type
A
soit lui-même un type générique.Malheureusement, c'est un peu verbeux et vous avez besoin de l'annotation @unchecked pour supprimer un avertissement du compilateur. Le TypeTag peut être incorporé dans la correspondance de modèle automatiquement par le compilateur à l'avenir: https://issues.scala-lang.org/browse/SI-6517
la source
[List String @unchecked]
car elle n'ajoute rien à cette correspondance de modèle (une simple utilisation lecase strlist if typeOf[A] =:= typeOf[String] =>
fera, ou mêmecase _ if typeOf[A] =:= typeOf[String] =>
si la variable liée n'est pas nécessaire dans le corps de lacase
).=>
soit exécuté. (Et lorsque le code sur le rhs est exécuté, les gardes fournissent une garantie statique sur le type des éléments. Il peut y avoir un casting là, mais c'est sûr.)Vous pouvez utiliser la
Typeable
classe de type de shapeless pour obtenir le résultat que vous recherchez,Exemple de session REPL,
L'
cast
opération sera aussi précise que possible en raison de l'effacementTypeable
disponible dans le champ d'application .la source
l1.cast[List[String]]
peu prèsfor (x<-l1) assert(x.isInstanceOf[String]
) Pour les grandes infrastructures de données ou si les transtypages se produisent très souvent, cela peut être une surcharge inacceptable.J'ai trouvé une solution relativement simple qui suffirait dans des situations d'utilisation limitée, enveloppant essentiellement les types paramétrés qui souffriraient du problème d'effacement de type dans les classes wrapper qui peuvent être utilisées dans une instruction de correspondance.
Cela a la sortie attendue et limite le contenu de notre classe de cas au type souhaité, String Lists.
Plus de détails ici: http://www.scalafied.com/?p=60
la source
Il existe un moyen de surmonter le problème d'effacement de type dans Scala. Dans Surmonter l'effacement de type dans la correspondance 1 et Surmonter l'effacement de type dans la correspondance 2 (variance) sont des explications sur la façon de coder certains assistants pour encapsuler les types, y compris la variance, pour la correspondance.
la source
J'ai trouvé une solution de contournement légèrement meilleure pour cette limitation du langage par ailleurs génial.
Dans Scala, le problème de l'effacement de type ne se produit pas avec les tableaux. Je pense qu'il est plus facile de le démontrer avec un exemple.
Disons que nous avons une liste de
(Int, String)
, puis ce qui suit donne un avertissement d'effacement de typePour contourner ce problème, créez d'abord une classe de cas:
puis dans la correspondance de motifs, faites quelque chose comme:
qui semble fonctionner parfaitement.
Cela nécessitera des modifications mineures dans votre code pour fonctionner avec des tableaux au lieu de listes, mais cela ne devrait pas être un problème majeur.
Notez que l'utilisation
case a:Array[(Int, String)]
donnera toujours un avertissement d'effacement de type, il est donc nécessaire d'utiliser une nouvelle classe de conteneur (dans cet exemple,IntString
).la source
Étant donné que Java ne connaît pas le type d'élément réel, j'ai trouvé qu'il était très utile de simplement l'utiliser
List[_]
. Ensuite, l'avertissement disparaît et le code décrit la réalité - c'est une liste de quelque chose d'inconnu.la source
Je me demande si c'est une solution de contournement adaptée:
Il ne correspond pas au cas de la "liste vide", mais il donne une erreur de compilation, pas un avertissement!
Cela semble d'autre part fonctionner ....
N'est-ce pas encore mieux ou est-ce que je manque le point ici?
la source
Pas une solution mais un moyen de vivre avec sans le balayer complètement sous le tapis: ajouter l'
@unchecked
annotation. Voir ici - http://www.scala-lang.org/api/current/index.html#scala.uncheckedla source
Je voulais ajouter une réponse qui généralise le problème à: Comment obtenir une représentation String du type de ma liste lors de l'exécution
la source
Utilisation de l'allumette de protection de motif
la source
isInstanceOf
effectue une vérification de l'exécution en fonction des informations de type disponibles pour la JVM. Et ces informations d'exécution ne contiendront pas l'argument typeList
(en raison de l'effacement de type).