Как мне обойти стирание типа на Scala? Или, почему я не могу получить параметр типа моих коллекций?
71152 просмотра
11 ответа
Печальный факт жизни в 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?!
Почему это так и как мне обойти это?
Автор: Daniel C. Sobral Источник Размещён: 12.09.2019 02:38Ответы (11)
241 плюса
В этом ответе используется
Manifest
-API, который устарел в Scala 2.10. Пожалуйста, смотрите ответы ниже для более актуальных решений.
Scala был определен с помощью типа Erasure, поскольку виртуальная машина Java (JVM), в отличие от Java, не получила обобщений. Это означает, что во время выполнения существует только класс, а не его параметры типа. В этом примере JVM знает, что он обрабатывает a scala.collection.immutable.List
, но не этот параметр параметризован Int
.
К счастью, в Scala есть функция, позволяющая обойти это. Это Манифест . Манифест - это класс, экземплярами которого являются объекты, представляющие типы. Поскольку эти экземпляры являются объектами, вы можете передавать их, хранить и вообще вызывать методы для них. С поддержкой неявных параметров он становится очень мощным инструментом. Возьмите следующий пример, например:
object Registry {
import scala.reflect.Manifest
private var map= Map.empty[Any,(Manifest[_], Any)]
def register[T](name: Any, item: T)(implicit m: Manifest[T]) {
map = map.updated(name, m -> item)
}
def get[T](key:Any)(implicit m : Manifest[T]): Option[T] = {
map get key flatMap {
case (om, s) => if (om <:< m) Some(s.asInstanceOf[T]) else None
}
}
}
scala> Registry.register("a", List(1,2,3))
scala> Registry.get[List[Int]]("a")
res6: Option[List[Int]] = Some(List(1, 2, 3))
scala> Registry.get[List[String]]("a")
res7: Option[List[String]] = None
При хранении элемента мы также храним его «Манифест». Манифест - это класс, экземпляры которого представляют типы Scala. Эти объекты содержат больше информации, чем JVM, что позволяет нам проверять полный параметризованный тип.
Обратите внимание, однако, что это Manifest
все еще развивающаяся особенность. Как пример его ограничений, в настоящее время он ничего не знает о дисперсии и предполагает, что все является ко-вариантом. Я ожидаю, что она станет более стабильной и надежной, когда библиотека отражений Scala, находящаяся в стадии разработки, будет готова.
93 плюса
Вы можете сделать это, используя TypeTags (как уже упоминал Даниэль, но я просто объясню это явно):
import scala.reflect.runtime.universe._
def matchList[A: TypeTag](list: List[A]) = list match {
case strlist: List[String @unchecked] if typeOf[A] =:= typeOf[String] => println("A list of strings!")
case intlist: List[Int @unchecked] if typeOf[A] =:= typeOf[Int] => println("A list of ints!")
}
Вы также можете сделать это, используя ClassTags (что избавляет вас от необходимости зависеть от scala-рефлекса):
import scala.reflect.{ClassTag, classTag}
def matchList2[A : ClassTag](list: List[A]) = list match {
case strlist: List[String @unchecked] if classTag[A] == classTag[String] => println("A List of strings!")
case intlist: List[Int @unchecked] if classTag[A] == classTag[Int] => println("A list of ints!")
}
ClassTags можно использовать до тех пор, пока вы не ожидаете, что параметр типа A
сам по себе является универсальным типом.
К сожалению, это немного многословно, и вам нужна аннотация @unchecked для подавления предупреждения компилятора. TypeTag может быть включен в сопоставление с образцом автоматически компилятором в будущем: https://issues.scala-lang.org/browse/SI-6517
Автор: tksfz Размещён: 08.02.2014 01:1963 плюса
Вы можете использовать Typeable
класс типов из бесформенного, чтобы получить результат, который вы ищете,
Образец сессии REPL,
scala> import shapeless.syntax.typeable._
import shapeless.syntax.typeable._
scala> val l1 : Any = List(1,2,3)
l1: Any = List(1, 2, 3)
scala> l1.cast[List[String]]
res0: Option[List[String]] = None
scala> l1.cast[List[Int]]
res1: Option[List[Int]] = Some(List(1, 2, 3))
cast
Операция будет столь же точным WRT стирания , насколько это возможно , учитывая в области видимости Typeable
экземпляры доступны.
15 плюса
Я придумал относительно простое решение, которое было бы достаточно в ситуациях ограниченного использования, по существу, для упаковки параметризованных типов, которые пострадали бы от проблемы стирания типов в классах-обертках, которые можно использовать в операторе сопоставления.
case class StringListHolder(list:List[String])
StringListHolder(List("str1","str2")) match {
case holder: StringListHolder => holder.list foreach println
}
Это имеет ожидаемый результат и ограничивает содержимое нашего класса case желаемым типом, String Lists.
Подробнее здесь: http://www.scalafied.com/?p=60
Автор: thricejamie Размещён: 14.06.2011 09:2914 плюса
В Scala есть способ преодолеть проблему стирания типов. В книге «Преодоление стирания типов при сопоставлении 1» и « Преодоление стирания типов при сопоставлении 2» (дисперсия) приводятся некоторые объяснения того, как закодировать некоторые помощники для переноса типов, включая дисперсию, для сопоставления.
Автор: Alex Размещён: 13.03.2011 02:2411 плюса
Я нашел немного лучший обходной путь для этого ограничения в остальном отличном языке.
В Scala проблема стирания типов не возникает с массивами. Я думаю, что это легче продемонстрировать на примере.
Допустим, у нас есть список (Int, String)
, тогда следующее выдает предупреждение об удалении типа
x match {
case l:List[(Int, String)] =>
...
}
Чтобы обойти это, сначала создайте класс case:
case class IntString(i:Int, s:String)
затем в сопоставлении с образцом сделайте что-то вроде:
x match {
case a:Array[IntString] =>
...
}
который, кажется, работает отлично.
Это потребует небольших изменений в вашем коде для работы с массивами вместо списков, но не должно быть серьезной проблемой.
Обратите внимание, что использование по- case a:Array[(Int, String)]
прежнему выдаст предупреждение об удалении типа, поэтому необходимо использовать новый контейнерный класс (в этом примере, IntString
).
6 плюса
Поскольку Java не знает фактический тип элемента, я нашел его наиболее полезным, чтобы просто использовать List[_]
. Затем предупреждение исчезает, и код описывает реальность - это список чего-то неизвестного.
4 плюса
Мне интересно, если это подходящий обходной путь:
scala> List(1,2,3) match {
| case List(_: String, _*) => println("A list of strings?!")
| case _ => println("Ok")
| }
Он не соответствует случаю «пустого списка», но выдает ошибку компиляции, а не предупреждение!
error: type mismatch;
found: String
requirerd: Int
Это с другой стороны, кажется, работает ....
scala> List(1,2,3) match {
| case List(_: Int, _*) => println("A list of ints")
| case _ => println("Ok")
| }
Разве это не даже лучше, или я здесь упускаю смысл?
Автор: agilesteel Размещён: 12.07.2011 09:361 плюс
Не решение, а способ жить с этим, не подметая его полностью: добавление @unchecked
аннотации. Смотрите здесь - http://www.scala-lang.org/api/current/index.html#scala.unchecked
0 плюса
Я хотел добавить ответ, который обобщает проблему: Как получить представление типа String типа моего списка во время выполнения
import scala.reflect.runtime.universe._
def whatListAmI[A : TypeTag](list : List[A]) = {
if (typeTag[A] == typeTag[java.lang.String]) // note that typeTag[String] does not match due to type alias being a different type
println("its a String")
else if (typeTag[A] == typeTag[Int])
println("its a Int")
s"A List of ${typeTag[A].tpe.toString}"
}
val listInt = List(1,2,3)
val listString = List("a", "b", "c")
println(whatListAmI(listInt))
println(whatListAmI(listString))
Автор: Steve Robinson
Размещён: 11.04.2018 08:50
-18 плюса
Использование паттерна
list match {
case x:List if x.isInstanceOf(List[String]) => do sth
case x:List if x.isInstanceOf(List[Int]) => do sth else
}
Автор: Huangmao Quan
Размещён: 03.07.2015 04:00
Вопросы из категории :
- scala Есть ли реальный опыт использования программной транзакционной памяти?
- scala Преобразование коллекции Java в коллекцию Scala
- scala Что означает один апостроф в Scala?
- scala Самый масштабируемый веб-стек для высокопроизводительного приложения Flash / Flex / AIR?
- scala Разбиение макета Scala на инфиксном операторе
- scala Как мне обойти стирание типа на Scala? Или, почему я не могу получить параметр типа моих коллекций?
- scala Перегрузка конструктора Scala?
- scala Scala-эквивалент Java java.lang.Class <T> Object
- scala Синтаксис Скала точка (или его отсутствие)
- scala Предпочтительный способ создания списка Scala
- type-erasure Стирание типа дженериков Java: когда и что происходит?
- type-erasure Java generics, почему я не могу создать экземпляр обобщенного типа
- type-erasure По какой причине я не могу создать универсальные типы массивов в Java?
- type-erasure Двойное определение Scala (2 метода имеют стирание одного типа)
- type-erasure Сериализация карты <Дата, строка> с Джексоном
- type-erasure Стирание Java с общей перегрузкой (без переопределения)
- type-erasure Каковы преимущества стирания типов Java?
- type-erasure Scala: шаблон абстрактного типа A не проверяется, поскольку он удаляется стиранием
- type-erasure Java - В чем разница между стиранием типа и выводом типа?