3

我正在解析一个包含各种结构的文件,其中一个是具有异构值的映射。将映射解析到内存后,我想根据值类型对其进行过滤,以获取给定类型的子映射。

为了方便起见,这里有一个简单的类比示例:

// the types I want to filter on
case class A(c: Char)
case class B(c: Char)

// an example for testing
val m: Map[Int, Any] = Map((1 -> A('x')), (2 -> B('p')),
                           (3 -> A('y')), (4 -> B('q')))

这是一个将地图过滤为 Map[Int, A] 的函数:

// a successful filter function based on A
def as(m: Map[Int, Any]): Map[Int, A] =
  for((int, a: A) <- m) yield (int -> a)

你可以想象实际上相同的函数“bs”也是成功的,但我不想写。相反,我想我会写一个通用函数:

// a failed generic filter function
def typeFilter[T](m: Map[Int, Any]): Map[Int, T] =
  for((int, t: T) <- m) yield (int -> t)

所以,这是状态:

val aMap: Map[Int, A] = as(m) // works!
val bMap: Map[Int, B] = bs(m) // works!
val aMapGen: Map[Int, A] = typedFilter[A](m) // doesn't work! returns all of m
val bMapGen: Map[Int, B] = typedFilter[B](m) // doesn't work! returns all of m

既然我对这个已经比较严谨了,要进入这个问题,就显得更加奇怪了。Map[Int, A] 如何包含到 B 值的映射?它按声明编译的事实似乎意味着它应该正常运行,但是当我打印 aMapGen 或 bMapGen 的内容时,我看到了 m 的全部内容,包括具有不兼容类型的值。这是我在 Scala 中遇到的第一个类似问题,就像 Java 中类型擦除的挫败感一样。

我很想解释一下为什么会这样,但我的主要目标是能够编写一些可重用的代码来根据类型进行过滤。否则,我将不得不为列表中的所有类型复制/粘贴具有更改类型的函数。

谢谢你的帮助。

4

3 回答 3

3

由于类型擦除,这失败了。Scala 在没有泛型的 Java 虚拟机上运行。因此,关于泛型的信息在运行时不可用,就像在 Java 中一样。

为了帮助解决这个问题,我建议您参考如何在 Scala 上解决类型擦除问题?或者,为什么我不能获取我的集合的类型参数?

于 2013-08-11T21:17:51.330 回答
3

这是一种改进吗?

scala> // an example for testing

scala> val m: Map[Int, Any] = Map((1 -> A('x')), (2 -> B('p')),
     |                            (3 -> A('y')), (4 -> B('q')))
m: Map[Int,Any] = Map(1 -> A(x), 2 -> B(p), 3 -> A(y), 4 -> B(q))

scala> def typeFilter[T](m: Map[Int, Any]): Map[Int, T] =
     |   for((int, t: T) <- m) yield (int -> t)
<console>:14: warning: abstract type pattern T is unchecked since it is eliminated by erasure
         for((int, t: T) <- m) yield (int -> t)
                      ^
typeFilter: [T](m: Map[Int,Any])Map[Int,T]

scala> import reflect._
import reflect._

scala> def typeFilter[T: ClassTag](m: Map[Int, Any]): Map[Int, T] =
     | for((int, t: T) <- m) yield (int -> t)
typeFilter: [T](m: Map[Int,Any])(implicit evidence$1: scala.reflect.ClassTag[T])Map[Int,T]

scala> typedFilter[A](m)
<console>:17: error: not found: value typedFilter
              typedFilter[A](m)
              ^

scala> typeFilter[A](m)
res3: Map[Int,A] = Map(1 -> A(x), 3 -> A(y))

scala> typeFilter[B](m)
res4: Map[Int,B] = Map(2 -> B(p), 4 -> B(q))

人们要求能够对特定类型的警告使用 -Xfatal-warnings。

在那一天到来之前,也许总是用 -Xlint -Xfatal-warnings 编译并用干净的床单铺床。

于 2013-08-12T00:01:21.587 回答
1
  1. 编译器不会在该行警告类型擦除吗

    for((int, t: T) <- m) yield (int -> t)
    

    ?

  2. 为了在运行时检查类,可以使用隐式 classTag:

    def typeFilter[T](m: Map[Int, Any])(implicit classTag:ClassTag[T]): Map[Int, T] =
      m.collect{ case (int, t:T) if classTag.isInstance(t) => (int -> t)}
    
于 2013-08-13T16:45:17.460 回答