使用scala泛型和manifest来在类中进行转换

时间:2014-02-05 20:39:51

标签: scala type-erasure erasure

我有两个班,Holders(目前缺少一个更好的名字)和Holder。 Holder必须通过Holders接口,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 个答案:

答案 0 :(得分:2)

你可以。

注意:Manifest已弃用,已替换为TypeTagClassTag,但这不会影响此答案的其余部分

通常在需要Manifest / TypeTag时,您确切地知道在编译时发生了什么,但是希望将这些信息保存到运行时。在这种情况下,您还必须处理这样一个事实:即使在编译时,_holders(index)也无法告诉您它返回的是什么类型的Holder

根据_holders的构建方式,可以将其替换为无形库中的Heterogeneous Map,这样就可以完全满足您的需求。< / p>

否则,您有正确的想法,在运行时测试类型。诀窍是使用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

答案 1 :(得分:2)

类型擦除使得这种事情......很难。简而言之,一旦编译了代码,所有类型参数都将替换为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,在这种情况下你希望提供的类型参数足够准确(考虑提供的类型参数可以是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 - 诚实!”,因为编译器天真,它相信你。