【问题标题】:scala : Match type argument for an objectscala :匹配对象的类型参数
【发布时间】:2013-11-21 16:06:01
【问题描述】:

如果我有一个接受类型参数的类,例如 Seq[T] ,并且我有很多此类的对象。我想根据类型参数拆分它们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 类而不匹配类型,我应该怎么做才能强制它匹配类型?

【问题讨论】:

    标签: scala pattern-matching


    【解决方案1】:

    您看到的情况是由于类型擦除 (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?

    【讨论】:

    • 我想更多的最新信息应该是关于TypeTags and their usage。:)
    • 有趣,还没有听说过!我会更新我的链接以指向它。
    • 我也可以提供一些链接:Scala 反射docs.scala-lang.org/overviews/reflection/overview.html 你需要异构列表。对于类型级编程(在你的情况下)有一个很棒的库:github.com/milessabin/shapeless,你可以找到很多有趣的结构。
    • 天哪!您不需要需要使用类型级编程,但您绝对可以。 Shapeless 就像 BFG9000 一样,当你完全了解后果时,两者都应该使用。
    【解决方案2】:

    TypeTag 方法

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

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

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

    当 scala 编译具有 (1) 类型 arg [T: TypeTag] 和 (2) 普通 arg someArg: T 的方法的调用时,它从调用上下文中收集 someArg 的类型参数元数据并增加类型 arg @987654326 @ 带有此元数据。 T 的值加上标签数据是从调用中推断出来的外部类型:

    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. 更智能的类型比较操作:

      通过typeOf[T](或typeTag[T].tpe)转换为Type。然后使用Type ops 的范围,包括模式匹配。注意:在反射类型空间中,=:= 表示类型等价(: 的类似物),<:< 表示类型一致性(<: 的类似物)

      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
        

    解决方案

    基于(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 - 但一旦分开,两者之间就没有更高阶的公共映射/折叠。 “真实世界”的案例不应该有这个问题。

    【讨论】:

      【解决方案3】:

      我认为def 每个序列类型具有多个原型的逻辑同样明智,而不是进入那些类型擦除解决方法。 2.10 编译器不会警告类型擦除,并且在运行时它似乎在我的情况下运行良好。

      大概这样可以避免问题,生成更易懂的代码。

      【讨论】:

        猜你喜欢
        • 2021-07-30
        • 1970-01-01
        • 2016-05-12
        • 2014-07-01
        • 1970-01-01
        • 2015-02-28
        • 2016-12-05
        • 2021-02-05
        • 2011-08-25
        相关资源
        最近更新 更多