首页 > 解决方案 > 试图理解 Scala 中的 ClassTag 和 TypeTag

问题描述

我试图了解我们如何在 scala 中使用ClassTagand来克服类型擦除TypeTag。我编写了以下示例,这些示例是通用函数,它们试图过滤掉List[TNode]where TNodeis equal to TMatch。但是,我希望在 中recognizeUsingTypeTag,函数不应调用 extractUsingClassTag是列表的通用类型等于TMatch(或消息被打印),但我的假设显然是错误的。谢谢你。

object Extractor {
  import scala.reflect.ClassTag

  def extractFail[TNode, TMatch](list: List[TNode]) = list.filter {
    case _: TMatch => true
    case _ => false
  }.map(x => x.asInstanceOf[TMatch])

  def extractUsingClassTag[TNode, TMatch](list: List[TNode])(implicit tag1: ClassTag[TNode], tag2: ClassTag[TMatch]) = list.filter {
    case _: TMatch => true
    case _ => false
  }.map(x => x.asInstanceOf[TMatch])

  import scala.reflect.runtime.universe._

  def recognizeUsingTypeTag[TNode, TMatch](list: List[TNode])(implicit tag1: TypeTag[TNode], tag2: TypeTag[TMatch], tag3: ClassTag[TNode], tag4: ClassTag[TMatch]) = list match {
    case _ if typeOf[TNode] =:= typeOf[TMatch] => {
      //
      // Why this does not get printed for List[String]
      //
      println("This should get printed when called for homogeneous")
      list.asInstanceOf[List[TMatch]]
    }
    case _ => extractUsingClassTag[TNode, TMatch](list)
  }
}

val homogeneous: List[String] = List("Hello", "World!")
val heterogeneous: List[Any] = List("Hello", "World!", 123, false)

println("extractFail")
println(Extractor.extractFail[Any, String](homogeneous))
println(Extractor.extractFail[Any, String](heterogeneous) + "\n")

println("extractUsingClassTag")
println(Extractor.extractUsingClassTag[Any, String](homogeneous))
println(Extractor.extractUsingClassTag[Any, String](heterogeneous) + "\n")

println("recognizeUsingTypeTag")
println(Extractor.recognizeUsingTypeTag[Any, String](homogeneous))
println(Extractor.recognizeUsingTypeTag[Any, String](heterogeneous) + "\n")

安慰:

extractFail
List(Hello, World!)
List(Hello, World!, 123, false)

extractUsingClassTag
List(Hello, World!)
List(Hello, World!)

recognizeUsingTypeTag
List(Hello, World!)
List(Hello, World!)

标签: scala

解决方案


为什么这不会为 List[String] 打印

因为您指定了显式类型参数:[Any, String],所以case _ if typeOf[TNode] =:= typeOf[TMatch]比较typeOf[Any] =:= typeOf[String].

由于您确实需要指定Stringfor TMatch,但希望TNode被推断,因此通常的方法是通过创建中间类将类型参数拆分为两个列表:

// in Extractor
class RecognizeUsingTypeTag[TMatch : TypeTag : ClassTag] {
  def apply[TNode : TypeTag : ClassTag](list: List[TNode]) = list match {
    case _ if typeOf[TNode] =:= typeOf[TMatch] => {
      //
      // Why this does not get printed for List[String]
      //
      println("This should get printed when called for homogeneous")
      list.asInstanceOf[List[TMatch]]
    }
    case _ => extractUsingClassTag[TNode, TMatch](list)
  }
}

def recognizeUsingTypeTag[TMatch : TypeTag : ClassTag] = new RecognizeUsingTypeTag[TMatch]


println(Extractor.recognizeUsingTypeTag[String].apply(homogeneous)) // inferred as apply[String]
println(Extractor.recognizeUsingTypeTag[String].apply(heterogeneous) + "\n") // inferred as apply[Any]

当您有这样的隐式参数并且不需要它们的名称时,最好使用上下文边界T : TypeTag : ClassTag添加两个类型的隐式参数TypeTag[T]ClassTag[T].


推荐阅读