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

How do I check if a Scala HigherKinded TypeTag is an Array?

我正在尝试将类型标记转换为 java class maintains/persists 通常擦除的类型参数。有相当多的库受益于此类转换(例如 Jackson 和 Guice)。我目前正在尝试将基于 Manifest 的代码迁移到 TypeTag,因为 Manifest 不足以应对某些特殊情况。

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

Manifest 实现很简单。 Manifest.erasure 是一个 Class 实例,其中 isArray() 已经是 valid/true。

TypeTag 实现比较棘手。没有快速简便的 erasure 方法。事实上,'similar' TypeTag 变体 RuntimeMirror.runtimeClass 不喜欢代表我们处理创建任何基于数组的 classes。阅读文档:

Note: If the Scala symbol is ArrayClass, a ClassNotFound exception is thrown because there is no unique Java class corresponding to a Scala generic array

为了解决这个问题,我尝试检测它是否是一个数组。如果它是一个数组,那么我手动创建 class 对象。但是,当 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 的类型参数为数组?

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

再次分解:

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

这可能会让你中途离开。

另一种方法是比较 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