2

我有两个班级,Holders(因为目前没有更好的名字)和 Holder。Holder 必须通过 Holders 接口,它有一个任意类型的 Holder 数组。因此,它必须采用 Any 类型。我想让 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]
    }
}
4

2 回答 2

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

当使用适当的选项编译时,这将产生警告。

在这种情况下,您有两个选择;您可以比较TypeTags,在这种情况下,您希望提供的类型参数足够准确(考虑提供的类型参数可以是 的任何超类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]做,这并不意味着“转换vT”。相反,您正在做的是说“哦,v是的,完全是T诚实的!”,并且因为编译器很天真,所以它相信您。

于 2014-02-05T22:21:13.010 回答
2

你可以。

注意:Manifest已弃用并替换为TypeTagand 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

于 2014-02-05T21:27:16.117 回答