在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?!
为什么会这样,我该如何解决呢?
我想知道这是否是一个合适的变通方案:
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")
| }
这样不是更好吗,还是我没抓住重点?
我提出了一个相对简单的解决方案,在有限的使用情况下就足够了,本质上是在可以在match语句中使用的包装器类中包装会遭受类型擦除问题的参数化类型。
case class StringListHolder(list:List[String])
StringListHolder(List("str1","str2")) match {
case holder: StringListHolder => holder.list foreach println
}
这具有预期的输出,并将case类的内容限制为所需的类型String Lists。
更多详情请访问:http://www.scalafied.com/?p=60
你可以从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中,数组不会出现类型擦除的问题。我认为用一个例子更容易说明这一点。
假设我们有一个(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)。