【问题标题】:Getting over type parameter bounds error with type checked by TypeTag使用 TypeTag 检查的类型克服类型参数边界错误
【发布时间】:2015-05-03 17:22:04
【问题描述】:

我想我已经接近开始理解类型擦除和 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 测试类型,然后我们做相应的事情。但它只有在你真的不需要确定你测试的类型时才有效......

例如,现在我有一个像这样的函数,具有类型参数边界和一个隐式参数,用于从函数参数中从 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

但现在我的问题是:如何在stupiderFunc 中匹配List[Set[_]] 的一般情况,以便将specialFunc 应用于arg,而不必列出所有特定的Int 或Long 类型?有没有办法在不修复内部类型参数的情况下使用asInstanceOf?或者有没有办法修复implicit 方法,以便在我们在match 中调用specialFunc 时发现args 实际上符合预期类型?

这是我可能使用宏来解决的问题吗?在这种情况下我应该如何处理?

【问题讨论】:

    标签: scala reflection type-inference type-erasure


    【解决方案1】:

    这是你想要的吗:

    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
    

    【讨论】:

    • 好的,这很好,但我只是设法创建了另一个不完全符合我需要的示例。我有 T 可以是容器的情况,也可以不是。在那种情况下T[Z] 没有意义。你怎么处理?我有一个case t if t =:= Function[X, Tuple[_,_]],然后是一个case _,然后我需要强制转换以调用该特殊类型的特定函数(返回元组的函数)。
    • 我会把它分成两个函数来处理。第一级 - 接受没有容器的参数,如果它是容器 - 然后调用第二个接受容器的参数
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-30
    • 1970-01-01
    • 2014-12-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-09
    • 1970-01-01
    相关资源
    最近更新 更多