scala:匹配对象的类型参数 [英] scala : Match type argument for an object
问题描述
如果我有一个接受 Type 参数的类,例如 Seq[T]
,并且我有很多这个类的对象.我想根据类型 Argument T
if i have a class that accepts a Type argument for example Seq[T]
, and i've many objects of this class. and i want to split them depending on 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
而不是 Type ,我应该怎么做才能强制它匹配 Type ?
the result of this code is StringString
.
it only matches the class Seq
not the Type also , what should i do to force it to match the Type ?
推荐答案
TypeTag Approach
Java 运行时需要泛型类型参数擦除.Scala 编译器通过将类型信息注入"到使用 TypeTag
类型参数声明的方法中来解决这个问题:
The Java runtime requires generic type param erasure. Scala compiler combats this by 'injecting' type info into methods declared with TypeTag
type arg:
def typeAwareMethod[T: TypeTag] (someArg: T) {
... // logic referring to T, the type of varToCheck
}
(或者,可以使用等效的、更冗长的隐式参数 - 未显示)
(alternatively, can use an equivalent, more long-winded implicit param - not shown)
当 scala 编译具有 (1) 类型 arg [T: TypeTag]
和 (2) 普通 arg someArg: T
的方法调用时,它收集类型参数来自调用上下文的 someArg
元数据,并使用此元数据扩充类型 arg T
.T 的值加上标签数据是从调用中外部类型推断的:
When scala compiles an invocation of a method having (1) type arg [T: TypeTag]
and (2) normal arg someArg: T
, it collects type param metadata for someArg
from the calling context and augments the type arg T
with this metadata. T's value plus tag data are externaly type-inferred from calls:
val slimesters = List[Reptile](new Frog(...), new CreatureFromBlackLagoon(...))
typeAwareMethod(slimesters)
引用 T 的逻辑(在上述方法中) - 运行时反射
import scala.reflection.runtime.universe._
:scala.relection.api.JavaUniverse
类型的 Universe 对象的内容.注意:受渐进式 API 更改的影响
import scala.reflection.runtime.universe._
: contents of the universe object of type scala.relection.api.JavaUniverse
. NB: subject to evolutionary API change
直接
TypeTag
对比:标签信息可以通过方法typeTag[T]
获得,然后直接测试/模式匹配与其他类型标签的(精确)相等性:
Direct
TypeTag
comparison: The tag info can be obtained via methodtypeTag[T]
, then directly tested/pattern matched for (exact) equality with other type tags:
val tag: TypeTag[T] = typeTag[T]
if (typeTag[T] == typeTag[List[Reptile]]) ...
typeTag[T] match {
case typeTag[List[Reptile]] => ...
}
限制:不知道子类型(以上不匹配List[Frog]
);无法通过 TypeTag
获得额外的元数据.
Limitations: not subtype aware (above won't match List[Frog]
); no additional metadata obtainable through TypeTag
.
更智能的类型比较操作:
Smarter Type-comparison operations:
通过typeOf[T]
(或typeTag[T].tpe
)转换为Type
.然后使用 Type
操作的范围,包括模式匹配.注意:在反射类型空间中,=:=
表示类型等效(类似于 :
),<:<
表示类型一致性(类似于 :
<:
)
Convert to Type
via typeOf[T]
(or typeTag[T].tpe
). Then use the gammut of Type
ops, including pattern-matching. NB: in reflection typespace, =:=
means type equivalance (analogue of :
), <:<
means type conformance (analogue of <:
)
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] 否则你将获得世界的类型擦除视图...
Method still needs type param [T: TypeTag] or you'll get the type-erasure view of the world...
反省类型元数据
我在 2 中撒了谎;).对于您的情况, typeOf[T]
实际上返回 TypeRef
(Type
的子类型),因为您正在实例化在别处声明的类型.要获得完整的元数据,您需要将 Type
转换为 TypeRef
.
I lied in 2 ;). For your case, typeOf[T]
actually returns TypeRef
(a subtype of Type
), since you're instantiating a type declared elsewhere. To get at the full metadata, you need to convert Type
to 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
,可以通过模式匹配提取部分:instead of
t: TypeRef
, can extract parts via pattern match on:case TypeRef(prefixType, typeSymbol, typeArgsListOfType) =>
Type
有方法:def typeSymbol: Symbol
Symbol 有方法:
Symbol has methods:
def fullName: String def name: Name
名称有方法:
Name has methods:
def decoded: String // the scala name def encoded: String // the java name
案例解决方案
基于(3)的解决方案:
Solution based on (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 ",") + "]")
<小时>
旁白:这个测试用例有一个问题:
Aside: there's an issue with this test case:
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 ,并且没有进一步的类型信息可用.为了获得更强的类型,通过单独的变量或元组/对来分离两个 Seqs - 但一旦分离,两者之间就没有更高阶的公共映射/折叠.真实世界"的案例不应该有这个问题.
Type of x is List[Seq[Any]]. Any is the lowest common ancestor of String and Int. In this case there's nothing to introspect, since all types descend from Any , and there's no further type information available. To get stronger typing, separate the two Seqs, either via separate variables or a tuple/pair - but once separated, no higher order common mapping / folding across the two. "Real world" cases shouldn't have this problem.
这篇关于scala:匹配对象的类型参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!