我在新的 2.10 版本中挖掘 Scala 的(有点稀疏?)文档时遇到了麻烦。我遇到一种情况,即我从没有类型信息的源递归读取数据。在阅读时,我知道预期的类型,因此我可以检查该类型是否与传入的数据对齐。
我的问题是:当尝试检索带有类型参数(例如Array[Int]
)的集合对象时,如何使用预期的类型来确保读取值的类型正确?
到目前为止,我已经摆弄了 Scala API 提供的代码,它允许您提取类型参数。我还阅读了有关类标签以及如何使用它们来创建数组的信息。所以我的下一个想法是 1) 找到类型参数,2) 从该类型创建一个数组,3) 查看读取的数据是否适合没有异常,如下所示:
val paramType = paramInfo[Array[X]] // Step 1: Find the X type parameter
val array = Array[paramType](size) // Step 2: Can't use TypeTags#Type as a normal Java type...
// Step 3: Feed data into the array and hope for the best
// Step 4: Profit!
由于上面的paramType
给了我 Type,因此将该类型转换为类标签应该是一个简单的问题。但答案说明了我。
老实说,这个解决方案对我来说似乎有点混乱,但我无法想出更聪明的东西。如果有替代解决方案,我全听!
提前谢谢。
编辑:为了澄清,我上面的例子应该证明我想从 Array[Int] 中提取类型 X(例如),然后创建一个包含该特定类型的数组实例。希望这让它更清楚。
编辑:也许需要进一步澄清(我真的不清楚吗?:-))。我想从数据源读取集合。我希望该集合使用正确的预期类型键入。因此,假设我将该方法称为readData
。由于我知道要期望什么类型,所以我给它一个预期类型的类型参数。举个例子,假设 Array[Int]。它可以是 Array[String] 或 Iterable[Any],或者只是 Null 或其他什么。调用 readData
方法时,我想将给定的预期类型(Array[Int])与从外部源读取的数据类型相匹配。如果找到的类型与预期类型相同,或者子类型,我们可以强制转换并返回数据。否则,将引发异常,通知用户找到的数据不是预期类型。所以总结一下:我如何拨打电话readData[Array[Int]]
工作?
编辑:我通过创建一个 Array[Any],检索预期的类型(上面的 X)并迭代数组以查看元素是否属于 X 的相同类型(或子类型)来解决问题。如果是,我们可以安全地转换为 Array[X]。在下面的示例中,预期类型由 E 表示。我知道这很黑客,但再次:我很想看到替代方案......
// Matches if the type of o corresponds (<:<) to the type of x
def verifyType(o : Any, x : Type) : Boolean = { ... }
// Get the type of the array parameter (X)
val tpe = reflect.runtime.universe.typeOf[E] tpe match {
// The returned Type is not an instance of universe.Type - hence the cast
case TypeRef(_, _, args) => args.asInstanceOf[List[Type]]
case _ => throw new IllegalArgumentException("Could not find type parameters in type " + tpe)
}
// Iterate the array and make sure the types match
val hasWrongType = array.exists(e => !verifyType(e, tpe))
if (hasWrongType) throw new Exception(...) // The types does not fit
else array.asInstanceOf[E] // We can safely cast
在 Scala 2.10 中,你真的不需要任何新的东西来做到这一点,尽管你使用替换的不是早期版本的ClassManifest
,ClassTag
:
def makeArray[T : reflect.ClassTag](length: Int): Array[T] = {
val tTag = implicitly[reflect.ClassTag[T]]
tTag.newArray(length)
}
在 REPL 中:
scala> makeArray[String](5)
res0: Array[String] = Array(null, null, null, null, null)
对于基元类型:
scala> makeArray[Int](5)
res1: Array[Int] = Array(0, 0, 0, 0, 0)
编辑:采取两个:
def makeArrayLike(a: Array[_], length: Int): Array[_] = {
val cA = a.getClass
val cC = cA.getComponentType
java.lang.reflect.Array.newInstance(cC, length).asInstanceOf[Array[_]]
}
在 REPL 中:
scala> val ai1 = Array[Int](1, 2, 3)
ai1: Array[Int] = Array(1, 2, 3)
scala> val as1 = Array[String]("one", "two", "three")
as1: Array[String] = Array(one, two, three)
scala> makeArrayLike(as1, 5)
res0: Array[_] = Array(null, null, null, null, null)
scala> makeArrayLike(ai1, 5)
res1: Array[_] = Array(0, 0, 0, 0, 0)