如果我有一个接受Type Seq[T]
的Type参数的类,并且我有很多这个类的对象。我想根据类型Argument T
例如:
val x = List(Seq[Int](1,2,3,4,5,6,7,8,9,0),Seq[String]("a","b","c"))
x.foreach { a =>
a match{
case _ : Seq[String] => print("String")
case _ : Seq[Int] => print("Int")
}
}
此代码的结果为StringString
。
它只匹配类Seq
而不匹配类型,我该怎么做才能强制它匹配类型?
答案 0 :(得分:5)
由于类型擦除(http://docs.oracle.com/javase/tutorial/java/generics/erasure.html),您所看到的情况发生了,某些IDE会警告您出现类似错误。
您可以查看清单,例如查看What is a Manifest in Scala and when do you need it?
编辑:像Patryk说的那样,TypeTag取代了Scala 2.10中的Manifest,见Scala: What is a TypeTag and how do I use it?
答案 1 :(得分:5)
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)normal arg someArg: T
的方法时,它会从调用上下文中收集someArg
的类型param元数据。使用此元数据扩充类型arg T
。 T&#39的值加标签数据是从调用中推断出的外部类型:
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
获得的其他元数据。
更智能的类型比较操作:
通过Type
(或typeOf[T]
)转换为typeTag[T].tpe
。然后使用Type
操作的gammut,包括模式匹配。注意:在反射类型空间中,=:=
表示类型等效(类似于:
),<:<
表示类型一致性(类似于<:
)
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,但是一旦分开,两者之间就没有更高阶的公共映射/折叠。 &#34;现实世界&#34;案件不应该有这个问题。
答案 2 :(得分:0)
我认为def
每个序列类型有多个原型的逻辑同样适用于那些类型擦除解决方法。 2.10编译器没有警告类型擦除,并且在运行时它似乎在我的情况下运行良好。
据推测,这可以避免这个问题,产生更易理解的代码。