在Scala中,如果你实例化一个List[Int],你可以验证你的实例是一个List,你可以验证它的任何单个元素是否是Int,但不能验证它是否是List[Int],这很容易验证:

scala> List(1,2,3) match {
     | case l : List[String] => println("A list of strings?!")
     | case _ => println("Ok")
     | }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!

unchecked选项直接将责任归咎于类型擦除:

scala>  List(1,2,3) match {
     |  case l : List[String] => println("A list of strings?!")
     |  case _ => println("Ok")
     |  }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
        case l : List[String] => println("A list of strings?!")
                 ^
A list of strings?!

为什么会这样,我该如何解决呢?


当前回答

这并不是解决方案,而是一种与之共存的方式,而不是完全掩盖它: 添加@unchecked注释。请看这里- http://www.scala-lang.org/api/current/index.html#scala.unchecked

其他回答

在Scala中有一种方法可以克服类型擦除问题。在克服匹配1中的类型擦除和克服匹配2中的类型擦除(方差)中,解释了如何编写一些帮助程序来包装类型,包括方差,以进行匹配。

由于Java不知道实际的元素类型,我发现使用List[_]最有用。然后警告消失了,代码描述了现实——它是一个未知事物的列表。

我想添加一个答案,将问题概括为:如何在运行时获得我的列表类型的String表示

import scala.reflect.runtime.universe._

def whatListAmI[A : TypeTag](list : List[A]) = {
    if (typeTag[A] == typeTag[java.lang.String]) // note that typeTag[String] does not match due to type alias being a different type
        println("its a String")
    else if (typeTag[A] == typeTag[Int])
        println("its a Int")

    s"A List of ${typeTag[A].tpe.toString}"
}

val listInt = List(1,2,3)
val listString = List("a", "b", "c")

println(whatListAmI(listInt))
println(whatListAmI(listString))

我找到了一个稍微好一点的解决方法来克服这门很棒的语言的局限性。

在Scala中,数组不会出现类型擦除的问题。我认为用一个例子更容易说明这一点。

假设我们有一个(Int, String)的列表,然后下面给出一个类型擦除警告

x match {
  case l:List[(Int, String)] => 
  ...
}

要解决这个问题,首先创建一个case类:

case class IntString(i:Int, s:String)

然后在模式匹配中执行如下操作:

x match {
  case a:Array[IntString] => 
  ...
}

这似乎很有效。

这将需要对代码进行一些小更改,以使用数组而不是列表,但这应该不是一个大问题。

注意,使用大小写a:Array[(Int, String)]仍然会给出类型擦除警告,因此有必要使用一个新的容器类(在本例中为IntString)。

你可以使用TypeTags (Daniel已经提到了,但我只是明确地拼写出来):

import scala.reflect.runtime.universe._
def matchList[A: TypeTag](list: List[A]) = list match {
  case strlist: List[String @unchecked] if typeOf[A] =:= typeOf[String] => println("A list of strings!")
  case intlist: List[Int @unchecked] if typeOf[A] =:= typeOf[Int] => println("A list of ints!")
}

你也可以使用ClassTags(这使你不必依赖scala-reflect):

import scala.reflect.{ClassTag, classTag}
def matchList2[A : ClassTag](list: List[A]) = list match {
  case strlist: List[String @unchecked] if classTag[A] == classTag[String] => println("A List of strings!")
  case intlist: List[Int @unchecked] if classTag[A] == classTag[Int] => println("A list of ints!")
}

只要不期望类型参数A本身是泛型类型,就可以使用ClassTags。

不幸的是,它有点啰嗦,您需要@unchecked注释来抑制编译器警告。将来编译器可能会自动将TypeTag合并到模式匹配中:https://issues.scala-lang.org/browse/SI-6517