以递归方式调用TypeTag函数

时间:2014-10-24 17:44:54

标签: scala recursion reflection

我正在玩Scala TypeTag。我想以递归方式调用带有TypeTag参数的函数。以下是我正在尝试做的简化示例:

import scala.reflect.runtime.universe._

object TypeTagTest extends App {

  def intValue[T](value: T)(implicit tag: TypeTag[T]): Int = {
    tag.tpe match {
      // integer
      case intType if intType <:< typeOf[Int] =>
        value.asInstanceOf[Int]
      // string
      case stringType if stringType <:< typeOf[String] =>
        value.asInstanceOf[String].toInt
      // option of either string or integer
      case optionType @ TypeRef(_, _, typeArg::Nil) if optionType <:< typeOf[Option[_]] =>
        println(s"Unwrapped type is $typeArg")
        val option = value.asInstanceOf[Option[_]]
        option.map { optionValue =>
          // how to pass the typeArg here?
          intValue(optionValue)
        }.getOrElse(0)
    }
  }

  println(intValue(1))
  println(intValue("1"))
  println(intValue(Some("1")))

}

此代码编译并运行:

1
1
Exception in thread "main" scala.MatchError: Any (of class scala.reflect.internal.Types$TypeRef$$anon$6)
    at TypeTagTest$.intValue(TypeTagTest.scala:7)
    at TypeTagTest$$anonfun$intValue$2.apply(TypeTagTest.scala:19)
    at TypeTagTest$$anonfun$intValue$2.apply(TypeTagTest.scala:18)
    at scala.Option.map(Option.scala:145)

几个问题:

  1. 如何在进行递归调用时传递类型信息?
  2. 有没有办法让这个模式匹配不那么难看?

1 个答案:

答案 0 :(得分:0)

这不是递归问题 - 您的“Unwrapped type ...”行永远不会打印。我怀疑问题在于optionType,在这种情况下Some[String],不是Option[_]的严格子类型,它扩展为Option[A] forSome {type A}

在这个特定的例子中,您可能希望测试optionType <:< Option[Any]是否Option,因为TypeRef是协变的?或许您可以检查Option的第一个参数是TypeTag

一旦你修复了它,我认为不可能获得参数的Type,因为它们总是由编译器生成,但是因为你只使用{{1}你可以让这个方法适用于那些:

def intValueInner(value: Any, tpe: Type) = tpe match {
  ...
  case optionType @ TypeRef(_, _, typeArg::Nil) if optionType <:< typeOf[Option[Any]] =>
    value.asInstanceOf[Option[_]].map(v => intValueInner(v, typeArg)).getOrElse(0)
}
def intValue[T: TypeTag](t: T) = intValueInner(t, typeOf[T])