简体   繁体   English

scala:对象的匹配类型参数

[英]scala : Match type argument for an object

if i have a class that accepts a Type argument for example Seq[T] , and i've many objects of this class. 如果我有一个类接受一个Type参数,例如Seq[T] ,我有很多这个类的对象。 and i want to split them depending on type Argument T 我想根据类型Argument T拆分它们

for example : 例如 :

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")  
   }
 }

the result of this code is StringString . 这段代码的结果是StringString it only matches the class Seq not the Type also , what should i do to force it to match the Type ? 它只匹配Seq类而不是Type类型,我该怎么做才能强制它匹配Type?

What you're seeing happens due to Type Erasure ( http://docs.oracle.com/javase/tutorial/java/generics/erasure.html ), some IDEs can warn you for errors like these. 你看到的是由于Type Erasure( http://docs.oracle.com/javase/tutorial/java/generics/erasure.html )而发生的,一些IDE会警告你这些错误。

You could have a look at Manifests, for example check out What is a Manifest in Scala and when do you need it? 您可以查看清单,例如查看Scala中的什么是Manifest,何时需要它?

Edit: like Patryk said, TypeTag replaced Manifest in Scala 2.10, see Scala: What is a TypeTag and how do I use it? 编辑:像Patryk说的那样,TypeTag取代了Scala 2.10中的Manifest,请参阅Scala:什么是TypeTag以及如何使用它?

TypeTag Approach TypeTag方法

The Java runtime requires generic type param erasure. Java运行时需要泛型类型参数擦除。 Scala compiler combats this by 'injecting' type info into methods declared with TypeTag type arg: Scala编译器通过将类型信息“注入”到使用TypeTag类型arg声明的方法来解决TypeTag

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) (或者,可以使用等效的,更长篇的隐式参数 - 未显示)

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. 当scala编译具有(1)类型arg [T: TypeTag]和(2)normal arg someArg: T的方法的调用时,它从调用上下文收集someArg类型param元数据,并使用此元数据扩充类型arg T T's value plus tag data are externaly type-inferred from calls: T的值加标签数据是从调用中推断出的外部类型:

val slimesters = List[Reptile](new Frog(...), new CreatureFromBlackLagoon(...))
typeAwareMethod(slimesters)

Logic Referring to T (within above method) - runtime reflection 逻辑参考T(在上面的方法中) - 运行时反射

import scala.reflection.runtime.universe._ : contents of the universe object of type scala.relection.api.JavaUniverse . import scala.reflection.runtime.universe._ :类型为scala.relection.api.JavaUniverse的Universe对象的scala.relection.api.JavaUniverse NB: subject to evolutionary API change 注意:受进化API变化的影响

  1. Direct TypeTag comparison: The tag info can be obtained via method typeTag[T] , then directly tested/pattern matched for (exact) equality with other type tags: 直接TypeTag比较:标签信息可以通过方法typeTag[T] ,然后直接测试/模式匹配与其他类型标签的(精确)相等:

     val tag: TypeTag[T] = typeTag[T] if (typeTag[T] == typeTag[List[Reptile]]) ... typeTag[T] match { case typeTag[List[Reptile]] => ... } 

    Limitations: not subtype aware (above won't match List[Frog] ); 限制:不能识别子类型(上面与List[Frog]不匹配); no additional metadata obtainable through TypeTag . 没有可通过TypeTag获得的其他元数据。

  2. Smarter Type-comparison operations: 更智能的类型比较操作:

    Convert to Type via typeOf[T] (or typeTag[T].tpe ). 通过typeOf[T] (或typeTag[T].tpe )转换为Type Then use the gammut of Type ops, including pattern-matching. 然后使用Type ops的gammut,包括模式匹配。 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!!! 

    Method still needs type param [T: TypeTag] or you'll get the type-erasure view of the world... 方法仍然需要类型参数[T:TypeTag]或者你将得到世界的类型擦除视图......

  3. Introspect on Type metadata 对类型元数据的反思

    I lied in 2 ;). 我撒谎2;)。 For your case, typeOf[T] actually returns TypeRef (a subtype of Type ), since you're instantiating a type declared elsewhere. 对于您的情况, typeOf[T]实际上返回TypeRefType的子Type ),因为您实例化了在其他地方声明的类型。 To get at the full metadata, you need to convert Type to TypeRef . 要获取完整元数据,您需要将Type转换为TypeRef

     typeTag[T].tpe match { case t: TypeRef => ... // call t.args to access typeArgs (as List[Type]) case _ => throw IllegalArgumentException("Not a TypeRef") } 
    • instead of t: TypeRef , can extract parts via pattern match on: 而不是t: TypeRef ,可以通过模式匹配提取零件:

        case TypeRef(prefixType, typeSymbol, typeArgsListOfType) => 
    • Type has method: Type有方法:

       def typeSymbol: 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 

Solution For Your Case 解决您的情况

Solution based on (3): 解决方案基于(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"))

Type of x is List[Seq[Any]]. x的类型是List [Seq [Any]]。 Any is the lowest common ancestor of String and Int. Any是String和Int的最低共同祖先。 In this case there's nothing to introspect, since all types descend from Any , and there's no further type information available. 在这种情况下,没有什么可以反省的,因为所有类型都来自Any,并且没有其他类型信息可用。 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. 为了获得更强的输入,可以通过单独的变量或元组/对来分离两个Seq,但是一旦分开,两者之间就没有更高阶的公共映射/折叠。 "Real world" cases shouldn't have this problem. “真实世界”案件不应该有这个问题。

I would argue it's equally sensible to def the logic with multiple prototypes one per sequence type, than go into those type-erasure workarounds. 我认为这是同样明智的def ,每个序列类型多个原型一个逻辑,不是去到这些类型擦除的解决方法。 The 2.10 compiler doesn't warn about type erasure, and at runtime it seems to work well in my case. 2.10编译器没有警告类型擦除,并且在运行时它似乎在我的情况下运行良好。

Presumably this avoids the problem, producing more intelligible code. 据推测,这可以避免问题,产生更易理解的代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM