Использование обобщенных scala и манифеста для приведения в классе
У меня есть два класса, Holders (из-за отсутствия лучшего названия на данный момент) и Holder. Holder должен быть связан через Holders, который имеет массив Holder любого типа. Как таковой, он должен принимать любой тип. Я хочу, чтобы setValue делала проверку типа, что вход Any действительно имеет тип T. Я немного читал об использовании манифестов, но я несколько теряюсь. Есть ли способ сделать то, что я хочу?
class Holders {
var values = Array[Any]()
var _holders = Array[Holder[_]]()
def setData(index: Int, newValue: Any) {
values(index) = newValue
_holders(index).setValue(newValue)
}
}
class Holder[T](someData: String, initValue: T) {
private var value : T = initValue
def getValue : T = value
def setValue(newValue: Any)(implicit m: Manifest[T]) = {
if (newValue.isInstanceOf[T])
value = newValue.asInstanceOf[T]
}
}
2 ответа
Вы можете.
нота: Manifest
устарела и заменена TypeTag
а также ClassTag
, но это не влияет на оставшуюся часть этого ответа
Часто, когда вам нужны Manifests/TypeTags, вы точно знаете, что происходит во время компиляции, но хотите, чтобы эта информация сохранялась и во время выполнения. В этом случае вам также приходится иметь дело с тем, что даже во время компиляции _holders(index)
не могу сказать вам, что за Holder
это возвращается.
В зависимости от того, как _holders
будет построен, возможно, будет возможно заменить его на Гетерогенную Карту из бесформенной библиотеки, которая будет делать именно то, что вам нужно из коробки.
В противном случае у вас есть правильная идея, тестирование типа во время выполнения. Трюк использует TypeTag
чтобы захватить как базовый тип держателя, так и тип нового значения.
Обратите внимание, что TypeTag
привязка контекста должна быть указана во всех вложенных методах, чтобы ее можно было передавать вниз по стеку вызовов в неявной области видимости. Присутствие TypeTag
это то, что позволяет typeOf
чтобы потом работать.
import scala.reflect.runtime.universe._ //for TypeTag
class Holders {
var values = Array[Any]()
var _holders = Array[Holder[_]]()
def setData[V: TypeTag](index: Int, newValue: V): Unit = {
values(index) = newValue
_holders(index).setValue(newValue)
}
}
class Holder[T: TypeTag](someData: String, initValue: T) {
private var value: T = initValue
def getValue: T = value
def setValue[V: TypeTag](newValue: V): Unit =
if(typeOf[V] <:< typeOf[T]) {
value = newValue.asInstanceOf[T]
}
Или используя Manifest
class Holder[T: Manifest](someData: String, initValue: T) {
private var value: T = initValue
def getValue: T = value
def setValue[V: Manifest](newValue: V): Unit =
if(manifest[V] <:< manifest[T]) {
value = newValue.asInstanceOf[T]
}
Я настоятельно призываю вас поддержать TypeTag
хоть!
Стирание типа делает такие вещи... сложными. В двух словах, после компиляции кода все параметры типа заменяются на Any
, Чтобы понять последствия, рассмотрим следующий пример:
trait Foo[T] { def isT(a: Any): Boolean = a.isInstanceOf[T] }
object Bar extends Foo[String]
Bar.isT("foo") // true
Bar.isT(42) // also true, as Int <: Any
Это создаст предупреждение при компиляции с соответствующими параметрами.
В этом сценарии у вас есть два варианта; вы можете сравнить TypeTag
s, и в этом случае вы надеетесь, что предоставленные параметры типа являются достаточно точными (рассмотрим предоставленный параметр типа, который может быть любым суперклассом value
), или вы сравниваете классы времени выполнения ваших значений (в этом случае вам не повезло при работе с универсальными типами). TypeTag
решение может выглядеть примерно так:
class Holder[T : TypeTag](someData: String, initValue: T) {
private var value = initValue
def setValue[V : TypeTag](v: V): Unit = {
// Works because there are TypeTags for T and V in implicit scope
if(typeOf[V] <:< typeOf[T])
value = v.asInstanceOf[T]
}
}
Теперь вы смотрите на это и говорите: "Ну, это не значит, что задание на самом деле value = v.asInstanceOf[Any]
?"и ответ - да value
также стирается в Any
, Кастинг ничего не делает, в том смысле, что v.asInstanceOf[T]
не значит "конвертировать" v
к T
"Вместо этого вы говорите:" О, да, v
полностью T
- Честно!", а поскольку компилятор наивен, он вам верит.