使用TypeTag

时间:2015-05-03 17:22:04

标签: scala reflection type-inference type-erasure

我认为我已经接近开始理解类型擦除和Scala反射API。但我还处在痛苦的世界......

我该如何处理这个问题?我有一个类可以将泛型函数作为参数,我需要根据函数输出中的类型做不同的事情。我还需要能够提取"类型。 (这与我的another question有关,这对我帮助很大,但仍然没有解决我的真正问题。)

我知道我可以做到以下几点:

import scala.reflect.runtime.universe._

object ReflectiveMysteryA extends App {

  def stupidFunc[A: TypeTag](arg: List[A]) = typeOf[A] match {
    case t if t =:= typeOf[List[String]] =>
      println("its a list of list of string")
    case t if t =:= typeOf[Set[Int]] =>
      println("its a list of set of int")
    case t if t =:= typeOf[Set[Long]] =>
      println("its a list of set of Long")
    case _ =>
      println("WTF")
  }

  stupidFunc(List(List("a", "bc"), List("b", "def")))
  stupidFunc(List(Set(1, 2), Set(2, 3, 4)))
  stupidFunc(List(Set(0L, 432L), Set(321L)))
  stupidFunc(List(Set("a", "bc", "def")))
  // stupidFunc(Set(Set("a", "bc", "def")))  // Doesn't compile, "type mismatch"
}
// its a list of list of string
// its a list of set of int
// its a list of set of Long
// WTF

这一切都很好,我们使用TypeTag测试类型,然后我们做相应的事情。但只有在你真的不需要确定你测试过的那种类型时它才有效......

例如,现在我有一个这样的函数,带有类型参数bounds和一个隐式参数,用于" extract"从函数参数类型LSB中键入B:

object ReflectiveMysteryB extends App {

  def specialFunc[LSB <: List[Set[_]], B: TypeTag](vv: LSB)(implicit ev: List[Set[B]] =:= LSB) = {
    typeOf[B] match {
      case t if t =:= typeOf[Int] =>
        println("list of set of int")
      case t if t =:= typeOf[Long] =>
        println("list of set of long")
    }
  }

  specialFunc(List(Set(1, 2), Set(2, 3, 4)))
  specialFunc(List(Set(0L, 432L), Set(321L)))
  // specialFunc(List(List("a", "bc"), List("b", "def"))) // Doesn't compile, "type parameter bounds"
}
// list of set of int
// list of set of long

这也很好。当我尝试撰写这两件事时会出现问题:

object ReflectiveMysteryC extends App {

  def stupiderFunc[A: TypeTag](arg: List[A]) = typeOf[A] match {
    case t if t =:= typeOf[List[String]] =>
      println("its a list of list of string")
    case t if t =:= typeOf[Set[Int]] =>
      specialFunc(arg)
    case t if t =:= typeOf[Set[Long]] =>
      specialFunc(arg)
    case _ =>
      println("WTF")
  }

  def specialFunc[LSB <: List[Set[_]], B: TypeTag](vv: LSB)(implicit ev: List[Set[B]] =:= LSB) = {
    typeOf[B] match {
      case t if t =:= typeOf[Int] =>
        println("list of set of int")
      case t if t =:= typeOf[Long] =>
        println("list of set of long")
    }
  }

  stupiderFunc(List(List("a", "bc"), List("b", "def")))
  stupiderFunc(List(Set(1, 2), Set(2, 3, 4)))
  stupiderFunc(List(Set(0L, 432L), Set(321L)))
  stupiderFunc(List(Set("a", "bc", "def")))
}

那不会编译

Error:(45, 7) inferred type arguments [List[A],Nothing] do not conform to method specialFunc's type parameter bounds [LSB <: List[Set[_]],B]
      specialFunc(arg)
      ^
Error:(45, 19) type mismatch;
 found   : List[A]
 required: LSB
      specialFunc(arg)
                  ^
Error:(45, 18) Cannot prove that List[Set[B]] =:= LSB.
      specialFunc(arg)
                 ^
Error:(47, 7) inferred type arguments [List[A],Nothing] do not conform to method specialFunc's type parameter bounds [LSB <: List[Set[_]],B]
      specialFunc(arg)
      ^
Error:(47, 19) type mismatch;
 found   : List[A]
 required: LSB
      specialFunc(arg)
                  ^
Error:(47, 18) Cannot prove that List[Set[B]] =:= LSB.
      specialFunc(arg)
                 ^

为了使其有效,我们可以执行以下操作:

object ReflectiveMysteryD extends App {

  def stupiderFunc[A: TypeTag](arg: List[A]) = typeOf[A] match {
    case t if t =:= typeOf[List[String]] =>
      println("its a list of list of string")
    case t if t =:= typeOf[Set[Int]] =>
      specialFunc(arg.asInstanceOf[List[Set[Int]]])
    case t if t =:= typeOf[Set[Long]] =>
      specialFunc(arg.asInstanceOf[List[Set[Long]]])
    case _ =>
      println("WTF")
  }

  def specialFunc[B: TypeTag](vv: List[Set[B]]) = {
    typeOf[B] match {
      case t if t =:= typeOf[Int] =>
        println("list of set of int")
      case t if t =:= typeOf[Long] =>
        println("list of set of long")
    }
  }

  stupiderFunc(List(List("a", "bc"), List("b", "def")))
  stupiderFunc(List(Set(1, 2), Set(2, 3, 4)))
  stupiderFunc(List(Set(0L, 432L), Set(321L)))
  stupiderFunc(List(Set("a", "bc", "def")))
}
// its a list of list of string
// list of set of int
// list of set of long
// WTF

但现在我的问题是:我如何匹配List[Set[_]]stupiderFunc的一般情况,以便将specialFunc应用于arg,而无需列出所有具体内容IntLong类型?有没有办法在不修复内部类型参数的情况下使用asInstanceOf?或者有没有办法修复implicit方法,以便在args内调用specialFunc时能够确定match实际符合预期类型}?

这是一个问题,我可能会使用宏来解决?在这种情况下我该怎么办?

1 个答案:

答案 0 :(得分:2)

这就是你想要的:

object NotAnyMoreMysteryD extends App {

  def stupiderFunc[A: TypeTag, Z: TypeTag, T[_]](arg: List[A])(implicit ev: A =:= T[Z]) = typeOf[A] match {
    case t if t <:< typeOf[List[_]] =>
      println("its a list of list of string")
    case t if t <:< typeOf[Set[_]] =>
      specialFunc(arg.asInstanceOf[List[Set[Z]]])
    case _ =>
      println("WTF")
  }

  def specialFunc[B: TypeTag](vv: List[Set[B]]) = {
    typeOf[B] match {
      case t if t =:= typeOf[Int] =>
        println("list of set of int")
      case t if t =:= typeOf[Long] =>
        println("list of set of long")
    }
  }

  stupiderFunc(List(List("a", "bc"), List("b", "def")))
  stupiderFunc(List(Set(1, 2), Set(2, 3, 4)))
  stupiderFunc(List(Set(0L, 432L), Set(321L)))
  stupiderFunc(List(Set("a", "bc", "def")))
}

替代:

def stupiderFunc[A[Z], Z: TypeTag](arg: List[A[Z]])(implicit tag: TypeTag[A[Z]]) = typeOf[A[Z]] match {
    case t if t <:< typeOf[List[_]] =>
      println("its a list of list of string")
    case t if t <:< typeOf[Set[_]] =>
      specialFunc(arg.asInstanceOf[List[Set[Z]]])
    case _ =>
      println("WTF")
}

结果:

scala>  stupiderFunc(List(List("a", "bc"), List("b", "def")))
its a list of list of string

scala> stupiderFunc(List(Set(1, 2), Set(2, 3, 4)))
list of set of int

scala> stupiderFunc(List(Set(0L, 432L), Set(321L)))
list of set of long

scala> stupiderFunc(List(Set("a", "bc", "def")))
scala.MatchError