在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?!
为什么会这样,我该如何解决呢?
我想添加一个答案,将问题概括为:如何在运行时获得我的列表类型的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))
你可以从shapeless中使用Typeable类型类来获得你想要的结果,
示例REPL会话,
scala> import shapeless.syntax.typeable._
import shapeless.syntax.typeable._
scala> val l1 : Any = List(1,2,3)
l1: Any = List(1, 2, 3)
scala> l1.cast[List[String]]
res0: Option[List[String]] = None
scala> l1.cast[List[Int]]
res1: Option[List[Int]] = Some(List(1, 2, 3))
给定作用域内可用的Typeable实例,强制转换操作将尽可能精确地执行wrt擦除。
我想知道这是否是一个合适的变通方案:
scala> List(1,2,3) match {
| case List(_: String, _*) => println("A list of strings?!")
| case _ => println("Ok")
| }
它不匹配“空列表”的情况,但它给出了一个编译错误,而不是警告!
error: type mismatch;
found: String
requirerd: Int
另一方面,这似乎工作....
scala> List(1,2,3) match {
| case List(_: Int, _*) => println("A list of ints")
| case _ => println("Ok")
| }
这样不是更好吗,还是我没抓住重点?
你可以使用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