[英]scala : Match type argument for an object
如果我有一个类接受一个Type参数,例如Seq[T]
,我有很多这个类的对象。 我想根据类型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
类而不是Type类型,我该怎么做才能强制它匹配Type?
你看到的是由于Type Erasure( http://docs.oracle.com/javase/tutorial/java/generics/erasure.html )而发生的,一些IDE会警告你这些错误。
您可以查看清单,例如查看Scala中的什么是Manifest,何时需要它?
编辑:像Patryk说的那样,TypeTag取代了Scala 2.10中的Manifest,请参阅Scala:什么是TypeTag以及如何使用它?
TypeTag方法
Java运行时需要泛型类型参数擦除。 Scala编译器通过将类型信息“注入”到使用TypeTag
类型arg声明的方法来解决TypeTag
:
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的值加标签数据是从调用中推断出的外部类型:
val slimesters = List[Reptile](new Frog(...), new CreatureFromBlackLagoon(...))
typeAwareMethod(slimesters)
逻辑参考T(在上面的方法中) - 运行时反射
import scala.reflection.runtime.universe._
:类型为scala.relection.api.JavaUniverse
的Universe对象的scala.relection.api.JavaUniverse
。 注意:受进化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的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
),因为您实例化了在其他地方声明的类型。 要获取完整元数据,您需要将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,但是一旦分开,两者之间就没有更高阶的公共映射/折叠。 “真实世界”案件不应该有这个问题。
我认为这是同样明智的def
,每个序列类型多个原型一个逻辑,不是去到这些类型擦除的解决方法。 2.10编译器没有警告类型擦除,并且在运行时它似乎在我的情况下运行良好。
据推测,这可以避免问题,产生更易理解的代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.