如何检查Scala HigherKinded TypeTag是否为数组?

时间:2015-01-22 15:11:21

标签: scala reflection

我试图将类型标记转换为维护/保留正常擦除类型参数的java类。有很多图书馆可以从像这样的转换中受益(比如Jackson和Guice)。我目前正在尝试将基于Manifest的代码迁移到TypeTag,因为Manifest不足以应对某些极端情况。

与其他数据类型相比,JVM将Arrays视为特殊对象。 classOf[Int]classOf[Array[Int]]之间的区别在于方法Class.isArray()将为后者返回true。

Manifest实施很简单。 Manifest.erasureClass个实例,其中isArray()已经有效/无效。

TypeTag实施比较棘手。没有快捷简便的erasure方法。事实上,类似的' TypeTag变体RuntimeMirror.runtimeClass不希望代表我们处理创建任何基于数组的类。阅读文档:

  

注意:如果Scala符号是ArrayClass,则为ClassNotFound异常   抛出因为没有与Scala对应的唯一Java类   通用数组

要解决这个问题,我尝试检测它是否是一个数组。如果是数组,那么我手动创建类对象。但是,当Array有一个未知的类型参数时,我遇到了一个额外的边缘情况。

首先让我向您展示一个不是HigherKinded类型的示例。

import scala.reflect.runtime.universe._
class A[T]

val innerType = typeOf[A[Array[_]]].asInstanceOf[TypeRefApi].args.head
innerType <:< typeOf[Array[_]] // Returns true.

到目前为止一切顺利。

class B[T[_]]

val innerType = typeOf[B[Array]].asInstanceOf[TypeRefApi].args.head
innerType <:< typeOf[Array[_]] // Returns false.

我无法创建一个typeOf [Array],因为它会抱怨缺少的参数。如何检测B的类型参数为Array?

此外,在这种情况下,类实例会是什么样子?它是一个数组[对象]吗?

2 个答案:

答案 0 :(得分:4)

再次分解:

scala> innerType match { case TypeRef(pre, sym, args) => sym == definitions.ArrayClass }
res13: Boolean = true

这可能让你分道扬。

答案 1 :(得分:0)

另一种方法是比较 typeConstructors:

import scala.reflect.runtime.universe._
class B[T[_]]

val innerType = typeOf[B[Array]].asInstanceOf[TypeRefApi].args.head
innerType.typeConstructor =:= typeOf[Array[_]].typeConstructor
innerType: reflect.runtime.universe.Type = Array
res4: Boolean = true

当我们需要检测类型是一个数组(任何类型)时,这通常也适用。

尝试获取此类innerType(比较)的擦除类型失败对于Array(而适用于其他HigherKinded类型):

class B[T[_]]

val innerType = typeOf[B[Array]].typeArgs.head
innerType.typeArgs
innerType.erasure // fails
innerType.erasure =:= typeOf[Array[_]].erasure
defined class B

innerType: reflect.runtime.universe.Type = Array
res4: List[reflect.runtime.universe.Type] = List()
java.util.NoSuchElementException: head of empty list
  at scala.collection.immutable.Nil$.head(List.scala:431)
  at scala.collection.immutable.Nil$.head(List.scala:428)
  at scala.reflect.internal.transform.Erasure$ErasureMap.apply(Erasure.scala:126)
  at scala.reflect.internal.transform.Transforms$class.transformedType(Transforms.scala:43)
  at scala.reflect.internal.SymbolTable.transformedType(SymbolTable.scala:16)
  at scala.reflect.internal.Types$TypeApiImpl.erasure(Types.scala:225)
  at scala.reflect.internal.Types$TypeApiImpl.erasure(Types.scala:218)
  ... 36 elided