TypeTag 方法
Java 运行时需要泛型类型参数擦除。 Scala 编译器通过将类型信息“注入”到使用 TypeTag 类型 arg 声明的方法中来解决这个问题:
def typeAwareMethod[T: TypeTag] (someArg: T) {
... // logic referring to T, the type of varToCheck
}
(或者,可以使用等效的、更冗长的隐式参数 - 未显示)
当 scala 编译具有 (1) 类型 arg [T: TypeTag] 和 (2) 普通 arg someArg: T 的方法的调用时,它从调用上下文中收集 someArg 的类型参数元数据并增加类型 arg @987654326 @ 带有此元数据。 T 的值加上标签数据是从调用中推断出来的外部类型:
val slimesters = List[Reptile](new Frog(...), new CreatureFromBlackLagoon(...))
typeAwareMethod(slimesters)
引用 T 的逻辑(在上述方法中) - 运行时反射
import scala.reflection.runtime.universe._:scala.relection.api.JavaUniverse 类型的 Universe 对象的内容。注意:受制于 API 的进化变化
-
直接TypeTag比较:
标签信息可以通过typeTag[T]方法获得,然后直接测试/模式匹配与其他类型标签的(精确)相等性:
val tag: TypeTag[T] = typeTag[T]
if (typeTag[T] == typeTag[List[Reptile]]) ...
typeTag[T] match {
case typeTag[List[Reptile]] => ...
}
限制:不知道子类型(以上内容与List[Frog] 不匹配);无法通过TypeTag 获得其他元数据。
-
更智能的类型比较操作:
通过typeOf[T](或typeTag[T].tpe)转换为Type。然后使用Type ops 的范围,包括模式匹配。注意:在反射类型空间中,=:= 表示类型等价(: 的类似物),<:< 表示类型一致性(<: 的类似物)
val tType: Type = typeOf[T] // or equivalently, typeTag[T].tpe
if (typeOf[T] <:< typeOf[List[Reptile]]) ... // matches List[Frog]
typeOf[T] match {
case t if t <:< typeOf[List[Reptile]] => ...
}
// Running Example:
def testTypeMatch[T: TypeTag](t: T) = if (typeOf[T] <:< typeOf[Seq[Int]]) "yep!!!"
test(List[Int](1, 2, 3)) // prints yep!!!
方法仍然需要类型参数 [T: TypeTag] 否则您将获得世界的类型擦除视图...
-
类型元数据自省
我在 2 中撒谎;)。对于您的情况,typeOf[T] 实际上返回 TypeRef(Type 的子类型),因为您正在实例化在别处声明的类型。要获得完整的元数据,您需要将 Type 转换为 TypeRef。
typeTag[T].tpe match {
case t: TypeRef => ... // call t.args to access typeArgs (as List[Type])
case _ => throw IllegalArgumentException("Not a TypeRef")
}
-
代替t: TypeRef,可以通过模式匹配来提取部分:
case TypeRef(prefixType, typeSymbol, typeArgsListOfType) =>
-
Type 有方法:
def typeSymbol: Symbol
-
符号有方法:
def fullName: String
def name: Name
-
名称有方法:
def decoded: String // the scala name
def encoded: String // the java name
解决方案
基于(3)的解决方案:
import scala.reflect.runtime.universe._
def typeArgsOf[T: TypeTag](a: T): List[Type] = typeOf[T] match {
case TypeRef(_, _, args) => args
case _ => Nil
}
val a = Seq[Int](1,2,3,4,5,6,7,8,9,0)
val b = Seq[String]("a","b","c")
// mkString & pring for debugging - parsing logic should use args, not strings!
print("[" + (typeArgsOf(a) mkString ",") + "]")
print("[" + (typeArgsOf(b) mkString ",") + "]")
旁白:这个测试用例有问题:
val x = List(Seq[Int](1,2,3,4,5,6,7,8,9,0),Seq[String]("a","b","c"))
x 的类型是 List[Seq[Any]]。 Any 是 String 和 Int 的最低共同祖先。在这种情况下,没有什么可反省的,因为所有类型都来自 Any ,并且没有更多可用的类型信息。为了获得更强的类型,请通过单独的变量或元组/对来分隔两个 Seq - 但一旦分开,两者之间就没有更高阶的公共映射/折叠。 “真实世界”的案例不应该有这个问题。