Печальный факт жизни в Scala заключается в том, что если вы создаете экземпляр List [Int], вы можете убедиться, что ваш экземпляр является списком, и вы можете проверить, что любой отдельный его элемент является Int, но не то, что это List [ Int], что легко проверить:
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?!
Опция -unchecked прямо обвиняет в стирании типов:
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?!
Почему это так, и как мне обойти это?
scala
type-erasure
Даниэль С. Собрал
источник
источник
TypeTag
с .scala 2.10.2
я увидел это предупреждение:<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?") ^
я считаю, что ваш вопрос и ответ очень полезны, но я не уверен, что это обновленное предупреждение полезно для читателей.Ответы:
Scala был определен с помощью типа Erasure, поскольку виртуальная машина Java (JVM), в отличие от Java, не получила обобщений. Это означает, что во время выполнения существует только класс, а не его параметры типа. В этом примере JVM знает, что он обрабатывает a
scala.collection.immutable.List
, но не этот параметр параметризованInt
.К счастью, в Scala есть функция, позволяющая обойти это. Это Манифест . Манифест - это класс, экземплярами которого являются объекты, представляющие типы. Поскольку эти экземпляры являются объектами, вы можете передавать их, хранить и вообще вызывать методы для них. С поддержкой неявных параметров он становится очень мощным инструментом. Возьмите следующий пример, например:
При хранении элемента мы также храним его «Манифест». Манифест - это класс, экземпляры которого представляют типы Scala. Эти объекты содержат больше информации, чем JVM, что позволяет нам проверять полный параметризованный тип.
Обратите внимание, однако, что это
Manifest
все еще развивающаяся особенность. Как пример его ограничений, в настоящее время он ничего не знает о дисперсии и предполагает, что все является ко-вариантом. Я ожидаю, что она станет более стабильной и надежной, когда библиотека отражений Scala, находящаяся в стадии разработки, будет готова.источник
get
Метод может быть определен какfor ((om, v) <- _map get key if om <:< m) yield v.asInstanceOf[T]
.TypeTag
на самом деле автоматически используются при сопоставлении с образцом? Круто, а?Manifest
параметр, см .: stackoverflow.com/a/11495793/694469 «экземпляр [manifest / type-tag] [...] неявно создается компилятором "Вы можете сделать это, используя TypeTags (как уже упоминал Даниэль, но я просто объясню это явно):
Вы также можете сделать это, используя ClassTags (что избавляет вас от необходимости зависеть от scala-рефлекса):
ClassTags можно использовать до тех пор, пока вы не ожидаете, что параметр типа
A
сам по себе является универсальным типом.К сожалению, это немного многословно, и вам нужна аннотация @unchecked для подавления предупреждения компилятора. TypeTag может быть включен в сопоставление с образцом автоматически компилятором в будущем: https://issues.scala-lang.org/browse/SI-6517
источник
[List String @unchecked]
как оно ничего не добавляет к этому сопоставлению с образцом (просто использованиеcase strlist if typeOf[A] =:= typeOf[String] =>
сделает это, или дажеcase _ if typeOf[A] =:= typeOf[String] =>
если связанная переменная не нужна в телеcase
).=>
. (И когда код на rhs выполняется, охранники предоставляют статическую гарантию на тип элементов. Там может быть приведение, но это безопасно.)Вы можете использовать
Typeable
класс типов из бесформенного, чтобы получить результат, который вы ищете,Образец сессии REPL,
cast
Операция будет столь же точным WRT стирания , насколько это возможно , учитывая в области видимостиTypeable
экземпляры доступны.источник
l1.cast[List[String]]
делает примерноfor (x<-l1) assert(x.isInstanceOf[String]
) Для больших структур данных или если преобразования происходят очень часто, это может быть недопустимым расходом.Я придумал относительно простое решение, которое было бы достаточно в ситуациях ограниченного использования, по существу, для упаковки параметризованных типов, которые пострадали бы от проблемы стирания типов в классах-обертках, которые можно использовать в операторе сопоставления.
Это имеет ожидаемый результат и ограничивает содержимое нашего класса case желаемым типом, String Lists.
Подробнее здесь: http://www.scalafied.com/?p=60
источник
В Scala есть способ преодолеть проблему стирания типов. В книге «Преодоление стирания типов при сопоставлении 1» и « Преодоление стирания типов при сопоставлении 2» (дисперсия) приводятся некоторые объяснения того, как закодировать некоторые помощники для переноса типов, включая дисперсию, для сопоставления.
источник
Я нашел немного лучший обходной путь для этого ограничения в остальном отличном языке.
В Scala проблема стирания типа не возникает с массивами. Я думаю, что это легче продемонстрировать на примере.
Допустим, у нас есть список
(Int, String)
, тогда следующее выдает предупреждение об удалении типаЧтобы обойти это, сначала создайте класс case:
затем в сопоставлении с образцом сделайте что-то вроде:
который, кажется, работает отлично.
Это потребует незначительных изменений в вашем коде для работы с массивами вместо списков, но не должно быть серьезной проблемой.
Обратите внимание, что использование по-
case a:Array[(Int, String)]
прежнему выдаст предупреждение об удалении типа, поэтому необходимо использовать новый класс контейнера (в этом примереIntString
).источник
Поскольку Java не знает фактический тип элемента, я нашел его наиболее полезным, чтобы просто использовать
List[_]
. Затем предупреждение исчезает, и код описывает реальность - это список чего-то неизвестного.источник
Мне интересно, если это подходящий обходной путь:
Он не соответствует случаю «пустого списка», но выдает ошибку компиляции, а не предупреждение!
Это с другой стороны, кажется, работает ....
Разве это не даже лучше, или я здесь упускаю смысл?
источник
Не решение, а способ жить с этим, не подметая его полностью: добавление
@unchecked
аннотации. Смотрите здесь - http://www.scala-lang.org/api/current/index.html#scala.uncheckedисточник
Я хотел добавить ответ, который обобщает проблему: Как получить представление типа String типа моего списка во время выполнения
источник
Использование паттерна
источник
isInstanceOf
метод не будет работать, заключается в том, что выполняется проверка во время выполнения на основе информации о типе, доступной для JVM. И эта информация во время выполнения не будет содержать аргумент типа дляList
(из-за стирания типа).