scala:对象的匹配类型参数

时间:2013-11-21 16:06:02

标签: scala pattern-matching

如果我有一个接受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而不匹配类型,我该怎么做才能强制它匹配类型?

3 个答案:

答案 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:

声明的方法中输入info
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变更的影响

  1. 直接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获得的其他元数据。

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

    通过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],否则您将获得世界的类型删除视图...

  3. 对类型元数据的反思

    我在2中撒谎;)。对于您的情况,typeOf[T]实际返回TypeRefType的子类型),因为您正在实例化其他地方声明的类型。要获取完整元数据,您需要将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
      
  4. 解决您的案件

    基于(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编译器没有警告类型擦除,并且在运行时它似乎在我的情况下运行良好。

据推测,这可以避免这个问题,产生更易理解的代码。