Так как Scala 2.7.2 есть что-то называемое Manifest
, которое является обходным способом для стирания типа Java. Но как Manifest
работает точно и почему/когда вам нужно его использовать?
Сообщение в блоге Manifests: Reified Types от Хорхе Ортиса объясняет некоторые из них, но не объясняет, как использовать его вместе с границы контекста.
Кроме того, что такое ClassManifest
, какая разница с Manifest
?
У меня есть некоторый код (часть более крупной программы, не могу легко включить его здесь), в котором есть некоторые предупреждения относительно стирания типа; Я подозреваю, что могу решить их, используя манифесты, но я точно не знаю, как именно.
Компилятор знает больше информации о типах, чем может просто представлять среда выполнения JVM. Манифест - это способ, которым компилятор может отправить межпространственное сообщение коду во время выполнения информации о типе информации, которая была потеряна.
Это похоже на то, как клептоны оставили закодированные сообщения в ископаемых записях и "мусорной" ДНК людей. Из-за ограничений полей скорости света и гравитационного резонанса они не могут напрямую связываться. Но, если вы знаете, как настроиться на их сигнал, вы можете воспользоваться способами, которые вы не можете себе представить, от принятия решения о том, что есть на обед или в каком количестве лото, чтобы играть.
Неясно, поможет ли манифест ошибок, которые вы видите, не зная подробностей.
Одним из общих применений Manifests является то, что ваш код ведет себя по-разному на основе статического типа коллекции. Например, что, если вы хотите рассматривать List [String] иначе, чем другие типы List:
def foo[T](x: List[T])(implicit m: Manifest[T]) = {
if (m <:< manifest[String])
println("Hey, this list is full of strings")
else
println("Non-stringy list")
}
foo(List("one", "two")) // Hey, this list is full of strings
foo(List(1, 2)) // Non-stringy list
foo(List("one", 2)) // Non-stringy list
Решение, основанное на отражении, могло бы включать проверку каждого элемента списка.
Контекстная привязка кажется наиболее подходящей для использования классов типов в scala, и это объясняется здесь Debasish Ghosh: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html
Контекстные границы также могут просто сделать сигнатуры метода более читабельными. Например, вышеупомянутая функция может быть переписана с использованием контекстных границ:
def foo[T: Manifest](x: List[T]) = {
if (manifest[T] <:< manifest[String])
println("Hey, this list is full of strings")
else
println("Non-stringy list")
}
Не полный ответ, но в отношении разницы между Manifest
и ClassManifest
вы можете найти пример в Scala 2.8 Array
бумага:
Единственный оставшийся вопрос - как реализовать создание общего массива. В отличие от Java, Scala позволяет создать экземпляр new
Array[T]
, гдеT
- это параметр типа. Как это можно реализовать, учитывая тот факт, что в Java нет единого представления массива?Единственный способ сделать это - потребовать дополнительную информацию о времени выполнения, которая описывает тип
T
. Scala 2.8 имеет новый механизм для этого, который называется Манифест. Объект типаManifest[T]
предоставляет полную информацию о типеT
.
ЗначенияManifest
обычно передаются в неявных параметрах; и компилятор знает, как их построить для статически известных типовT
.Существует также более слабая форма с именем
ClassManifest
, которая может быть создана из знания только класса верхнего уровня типа, не обязательно знающего все его типы аргументов.
Именно этот тип информации о времени выполнения требуется для создания массива.
Пример:
Необходимо предоставить эту информацию, передав
ClassManifest[T]
в метод как неявный параметр:
def tabulate[T](len:Int, f:Int=>T)(implicit m:ClassManifest[T]) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
В качестве сокращенной формы контекст bound1 может использоваться вместо параметра типа
T
,
(см. этот SO вопрос для иллюстрации)
давая:
def tabulate[T: ClassManifest](len:Int, f:Int=>T) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
При вызове tabulate для типа типа
Int
илиString
илиList[T]
компилятор Scala может создать манифест класса, чтобы передать как неявный аргумент для табуляции.
Манифест был предназначен для восстановления типичных типов, которые стираются для запуска на JVM (который не поддерживает дженерики). Однако у них были некоторые серьезные проблемы: они были слишком упрощенными и не могли полностью поддерживать систему типов Scala. Таким образом, они были устаревшими в Scala 2.10 и заменены на TypeTag
(которые, по существу, используются самим компилятором Scala для представления типов и, следовательно, полностью поддерживают типы Scala). Подробнее о различии см. Ниже:
когда вам это нужно?
До 2013-01-04 когда был выпущен Scala 2.10.
Пусть также выделяет manifest
в scala
источниках (Manifest.scala
), мы видим:
Manifest.scala:
def manifest[T](implicit m: Manifest[T]) = m
Итак, что касается следующего примера кода:
def foo[A](somelist: List[A])(implicit m: Manifest[A]): String = {
if (m <:< manifest[String]) {
"its a string"
} else {
"its not a string"
}
}
мы видим, что manifest
function
ищет неявный m: Manifest[T]
, который удовлетворяет type parameter
, который вы указываете в нашем примере кода, который был manifest[String]
. Поэтому, когда вы вызываете что-то вроде:
if (m <:< manifest[String]) {
вы проверяете, соответствует ли текущий implicit m
, который вы определили в вашей функции, тип manifest[String]
, а поскольку manifest
является функцией типа manifest[T]
, он будет искать конкретный manifest[String]
, и он будет найти, если есть такое неявное.