scala:对象的匹配类型参数

Had*_*har 2 scala pattern-matching

如果我有一个接受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")  
   }
 }
Run Code Online (Sandbox Code Playgroud)

这段代码的结果是 StringString.它只匹配类Seq而不是类型,我应该怎么做才能强制它匹配类型?

Seb*_*oek 5

你看到的是由于Type Erasure(http://docs.oracle.com/javase/tutorial/java/generics/erasure.html),有些IDE会警告你这些错误.

您可以查看清单,例如查看Scala中的什么是清单以及何时需要它?

编辑:像Patryk说的那样,TypeTag取代了Scala 2.10中的Manifest,请参阅Scala:什么是TypeTag以及如何使用它?


Gle*_*est 5

TypeTag方法

Java运行时需要泛型类型参数擦除.Scala编译器通过将类型信息"注入"到使用TypeTagarg类型声明的方法来解决这个问题:

def typeAwareMethod[T: TypeTag] (someArg: T) { 
  ... // logic referring to T, the type of varToCheck
}
Run Code Online (Sandbox Code Playgroud)

(或者,可以使用等效的,更长篇的隐式参数 - 未显示)

当scala编译具有(1)类型arg [T: TypeTag]和(2)normal arg 的方法的调用时someArg: T,它someArg从调用上下文收集类型param元数据,并T使用此元数据扩充类型arg .T的值加标签数据是从调用中推断出的外部类型:

val slimesters = List[Reptile](new Frog(...), new CreatureFromBlackLagoon(...))
typeAwareMethod(slimesters)
Run Code Online (Sandbox Code Playgroud)

逻辑参考T(在上面的方法中) - 运行时反射

import scala.reflection.runtime.universe._:类型的Universe对象的内容scala.relection.api.JavaUniverse.注意:受进化API变化的影响

  1. 直接TypeTag比较:标签信息可以通过方法获得typeTag[T],然后直接测试/模式匹配与其他类型标签的(精确)相等:

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

    限制:不能识别子类型(上面不匹配List[Frog]); 没有可通过的其他元数据获得TypeTag.

  2. 更智能的类型比较操作:

    转换为Typevia typeOf[T](或typeTag[T].tpe).然后使用Typeops 的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!!!
    
    Run Code Online (Sandbox Code Playgroud)

    方法仍然需要类型参数[T:TypeTag]或者你将得到世界的类型擦除视图......

  3. 对类型元数据的反思

    我撒谎2;).对于您的情况,typeOf[T]实际上返回TypeRef(子类型Type),因为您实例化在其他地方声明的类型.要获取完整元数据,您需要转换TypeTypeRef.

    typeTag[T].tpe match {
      case t: TypeRef => ... // call t.args to access typeArgs (as List[Type])
      case _ => throw IllegalArgumentException("Not a TypeRef")
    }
    
    Run Code Online (Sandbox Code Playgroud)

解决您的情况

解决方案基于(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 ",") + "]")
Run Code Online (Sandbox Code Playgroud)

旁白:此测试用例存在问题:

val x = List(Seq[Int](1,2,3,4,5,6,7,8,9,0),Seq[String]("a","b","c"))
Run Code Online (Sandbox Code Playgroud)

x的类型是List [Seq [Any]].Any是String和Int的最低共同祖先.在这种情况下,没有什么可以反省的,因为所有类型都来自Any,并且没有其他类型信息可用.为了获得更强的输入,可以通过单独的变量或元组/对来分离两个Seq,但是一旦分开,两者之间就没有更高阶的公共映射/折叠."真实世界"案件不应该有这个问题.