É um fato triste da vida no Scala que, se você instanciar uma lista [Int], pode verificar se sua instância é uma lista e se qualquer elemento individual dela é um Int, mas não é uma lista [ Int], como pode ser facilmente verificado:
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?!
A opção -unchecked coloca a culpa diretamente no apagamento de tipo:
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 que é isso e como faço para contornar isso?
scala
type-erasure
Daniel C. Sobral
fonte
fonte
TypeTag
s .scala 2.10.2
, vi esse aviso:<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?") ^
acho sua pergunta e resposta muito úteis, mas não tenho certeza se esse aviso atualizado é útil para os leitores.Respostas:
Scala foi definido com o Type Erasure porque a Java Virtual Machine (JVM), ao contrário do Java, não obteve genéricos. Isso significa que, no tempo de execução, apenas a classe existe, não seus parâmetros de tipo. No exemplo, a JVM sabe que está manipulando a
scala.collection.immutable.List
, mas não com a qual esta lista está parametrizadaInt
.Felizmente, há um recurso no Scala que permite contornar isso. É o manifesto . Um manifesto é uma classe cujas instâncias são objetos que representam tipos. Como essas instâncias são objetos, você pode distribuí-las, armazená-las e geralmente chamar métodos sobre elas. Com o suporte de parâmetros implícitos, torna-se uma ferramenta muito poderosa. Veja o exemplo a seguir, por exemplo:
Ao armazenar um elemento, também armazenamos um "Manifesto". Um manifesto é uma classe cujas instâncias representam os tipos Scala. Esses objetos têm mais informações do que a JVM, o que nos permite testar o tipo completo e parametrizado.
Observe, no entanto, que a
Manifest
ainda é um recurso em evolução. Como exemplo de suas limitações, atualmente ele não sabe nada sobre variação e assume que tudo é co-variante. Espero que fique mais estável e sólido assim que a biblioteca de reflexão Scala, atualmente em desenvolvimento, for concluída.fonte
get
método pode ser definido comofor ((om, v) <- _map get key if om <:< m) yield v.asInstanceOf[T]
.TypeTag
são realmente usados automaticamente na correspondência de padrões? Legal, né?Manifest
próprio parâmetro, consulte: stackoverflow.com/a/11495793/694469 "a instância [manifest / tag de tipo] [...] está sendo criada implicitamente pelo compilador "Você pode fazer isso usando TypeTags (como Daniel já menciona, mas eu explicarei explicitamente):
Você também pode fazer isso usando ClassTags (que evita que você precise depender do scala-reflect):
ClassTags podem ser usadas desde que você não espere que o parâmetro type
A
seja um tipo genérico.Infelizmente, é um pouco detalhado e você precisa da anotação @unchecked para suprimir um aviso do compilador. O TypeTag pode ser incorporado à correspondência de padrões automaticamente pelo compilador no futuro: https://issues.scala-lang.org/browse/SI-6517
fonte
[List String @unchecked]
pois ele não adiciona nada a essa correspondência de padrões (apenas o uso ocase strlist if typeOf[A] =:= typeOf[String] =>
fará, ou mesmocase _ if typeOf[A] =:= typeOf[String] =>
se a variável vinculada não for necessária no corpo docase
).=>
seja executado. (E quando o código sobre os RHS é executado, os guardas fornecer uma garantia estática do tipo de elementos Pode haver um elenco lá, mas é seguro..)Você pode usar a
Typeable
classe type de informe para obter o resultado desejado,Exemplo de sessão REPL,
A
cast
operação será a mais precisa possível de apagamento, considerando asTypeable
instâncias disponíveis no escopo .fonte
l1.cast[List[String]]
funciona aproximadamentefor (x<-l1) assert(x.isInstanceOf[String]
) Para grandes estruturas de dados ou se as transmissões ocorrem com muita frequência, isso pode ser uma sobrecarga inaceitável.Eu vim com uma solução relativamente simples que seria suficiente em situações de uso limitado, essencialmente agrupando tipos parametrizados que sofreriam com o problema de apagamento de tipo nas classes de invólucro que podem ser usadas em uma declaração de correspondência.
Isso tem a saída esperada e limita o conteúdo de nossa classe de caso ao tipo desejado, String Lists.
Mais detalhes aqui: http://www.scalafied.com/?p=60
fonte
Existe uma maneira de superar o problema de apagamento de tipo no Scala. Em Superando apagamento de tipo na correspondência 1 e Superando apagamento de tipo na correspondência 2 (variação), há algumas explicações sobre como codificar alguns auxiliares para agrupar os tipos, incluindo variação, para correspondência.
fonte
Encontrei uma solução um pouco melhor para essa limitação da linguagem impressionante.
Em Scala, o problema do apagamento de tipo não ocorre com matrizes. Eu acho que é mais fácil demonstrar isso com um exemplo.
Digamos que temos uma lista de
(Int, String)
, então o seguinte fornece um aviso de apagamento de tipoPara contornar isso, primeiro crie uma classe de caso:
então, na correspondência de padrões, faça algo como:
o que parece funcionar perfeitamente.
Isso exigirá pequenas alterações no seu código para trabalhar com matrizes em vez de listas, mas não deve ser um grande problema.
Observe que o uso
case a:Array[(Int, String)]
ainda emitirá um aviso de apagamento de tipo; portanto, é necessário usar uma nova classe de contêiner (neste exemploIntString
).fonte
Como o Java não conhece o tipo de elemento real, achei mais útil apenas usar
List[_]
. Então o aviso desaparece e o código descreve a realidade - é uma lista de algo desconhecido.fonte
Gostaria de saber se esta é uma solução adequada:
Não corresponde ao caso "lista vazia", mas gera um erro de compilação, não um aviso!
Por outro lado, isso parece funcionar ....
Não é ainda melhor ou estou perdendo o objetivo aqui?
fonte
Não é uma solução, mas uma maneira de conviver com ela sem varrê-la para baixo do tapete: adicionando a
@unchecked
anotação. Veja aqui - http://www.scala-lang.org/api/current/index.html#scala.uncheckedfonte
Eu queria adicionar uma resposta que generalize o problema para: Como obter uma representação String do tipo da minha lista em tempo de execução
fonte
Usando guarda de correspondência de padrão
fonte
isInstanceOf
faz uma verificação de tempo de execução com base nas informações de tipo disponíveis para a JVM. E essas informações de tempo de execução não conterão o argumento de tipo paraList
(por causa do apagamento do tipo).