Использование обобщенных 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 - Честно!", а поскольку компилятор наивен, он вам верит.

Другие вопросы по тегам