Dan*_*ral 366 scala type-erasure
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?!
Run Code Online (Sandbox Code Playgroud)
-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?!
Run Code Online (Sandbox Code Playgroud)
为什么会这样,我该如何解决它?
Dan*_*ral 241
此答案使用
Manifest-API,自Scala 2.10起已弃用.请查看以下答案以获取更多当前解决方案
Scala是使用Type Erasure定义的,因为与Java不同,Java虚拟机(JVM)没有得到泛型.这意味着,在运行时,只存在类,而不是类型参数.在该示例中,JVM知道它正在处理a scala.collection.immutable.List,但不是说该列表是参数化的Int.
幸运的是,Scala中有一个功能可以让你解决这个问题.这是清单.Manifest是一个类,其实例是表示类型的对象.由于这些实例是对象,因此您可以传递它们,存储它们,并且通常在它们上调用方法.在隐式参数的支持下,它成为一个非常强大的工具.以下面的示例为例:
object Registry {
import scala.reflect.Manifest
private var map= Map.empty[Any,(Manifest[_], Any)]
def register[T](name: Any, item: T)(implicit m: Manifest[T]) {
map = map.updated(name, m -> item)
}
def get[T](key:Any)(implicit m : Manifest[T]): Option[T] = {
map get key flatMap {
case (om, s) => if (om <:< m) Some(s.asInstanceOf[T]) else None
}
}
}
scala> Registry.register("a", List(1,2,3))
scala> Registry.get[List[Int]]("a")
res6: Option[List[Int]] = Some(List(1, 2, 3))
scala> Registry.get[List[String]]("a")
res7: Option[List[String]] = None
Run Code Online (Sandbox Code Playgroud)
存储元素时,我们也存储它的"清单".Manifest是一个类,其实例代表Scala类型.这些对象比JVM具有更多信息,这使我们能够测试完整的参数化类型.
但请注意,a Manifest仍然是一个不断发展的功能.作为其局限性的一个例子,它目前对方差一无所知,并假设一切都是共变的.一旦目前正在开发的Scala反射库完成,我预计它将变得更加稳定和稳定.
tks*_*sfz 93
你可以使用TypeTags来做这件事(正如丹尼尔已经提到的那样,但我只是明确拼写出来):
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!")
}
Run Code Online (Sandbox Code Playgroud)
您也可以使用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!")
}
Run Code Online (Sandbox Code Playgroud)
只要您不希望type参数A本身是泛型类型,就可以使用ClassTags .
不幸的是,它有点冗长,你需要@unchecked注释来抑制编译器警告.TypeTag可能会在未来由编译器自动合并到模式匹配中:https://issues.scala-lang.org/browse/SI-6517
Mil*_*bin 63
您可以使用无形的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))
Run Code Online (Sandbox Code Playgroud)
cast考虑到Typeable可用的范围内实例,操作将尽可能精确地擦除.
小智 15
我提出了一个相对简单的解决方案,在有限的使用情况下就足够了,实际上包装了参数化类型,这些类型会受到可在匹配语句中使用的包装类中的类型擦除问题的影响.
case class StringListHolder(list:List[String])
StringListHolder(List("str1","str2")) match {
case holder: StringListHolder => holder.list foreach println
}
Run Code Online (Sandbox Code Playgroud)
这具有预期的输出,并将我们的case类的内容限制为所需的类型String Lists.
更多细节:http://www.scalafied.com/?p = 60
Ale*_*lex 14
有一种方法可以克服Scala中的类型擦除问题.在匹配1中克服类型擦除和匹配中的克服类型擦除2(方差)是对如何编码一些帮助以包装类型(包括方差)进行匹配的一些解释.
Jus*_*s12 11
我找到了一个稍微更好的解决方法来解决这个非常棒的语言的限制.
在Scala中,阵列不会出现类型擦除问题.我认为通过一个例子更容易证明这一点.
让我们说我们有一个列表(Int, String),然后下面给出了类型擦除警告
x match {
case l:List[(Int, String)] =>
...
}
Run Code Online (Sandbox Code Playgroud)
要解决此问题,请首先创建一个案例类:
case class IntString(i:Int, s:String)
Run Code Online (Sandbox Code Playgroud)
然后在模式匹配中执行以下操作:
x match {
case a:Array[IntString] =>
...
}
Run Code Online (Sandbox Code Playgroud)
这看起来很完美.
这将需要对代码进行细微更改以使用数组而不是列表,但不应该是主要问题.
请注意,using case a:Array[(Int, String)]仍将提供类型擦除警告,因此必须使用新的容器类(在此示例中IntString).
| 归档时间: |
|
| 查看次数: |
71916 次 |
| 最近记录: |