scala:匹配对象的类型参数 [英] scala : Match type argument for an object

查看:42
本文介绍了scala:匹配对象的类型参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我有一个接受 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

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

  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:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆