在Scala上生活的一個可悲的事實是,若是實例化一個List [Int],則能夠驗證您的實例是一個List,而且能夠驗證它的任何單個元素是一個Int,而不是它是一個List [能夠很容易地驗證: html
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選項將責任徹底歸咎於類型擦除: es6
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?!
爲何會這樣,我該如何解決? api
該答案使用
Manifest
-API,從Scala 2.10開始不推薦使用。 請參閱下面的答案以獲取更多最新解決方案。
Scala是用Type Erasure定義的,由於與Java不一樣,Java虛擬機(JVM)沒有泛型。 這意味着,在運行時,僅存在類,而不存在其類型參數。 在該示例中,JVM知道它正在處理scala.collection.immutable.List
,但不是該列表是使用Int
參數化的。 工具
幸運的是,Scala中有一項功能可讓您解決這個問題。 這是清單 。 清單是類,其實例是表示類型的對象。 因爲這些實例是對象,所以您能夠傳遞它們,存儲它們並一般在它們上調用方法。 在隱式參數的支持下,它成爲一個很是強大的工具。 以如下示例爲例: 測試
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
當存儲一個元素時,咱們也存儲它的「清單」。 清單是一個類,其實例表示Scala類型。 這些對象比JVM具備更多信息,這使咱們可以測試完整的參數化類型。 spa
可是請注意, Manifest
還是一個不斷髮展的功能。 做爲其侷限性的一個例子,它目前對方差一無所知,並假設一切都是協變的。 我但願一旦開發中的Scala反射庫完成,它將變得更加穩定和可靠。 .net
因爲Java不知道實際的元素類型,所以我發現僅使用List[_]
最爲有用。 而後警告消失,代碼描述了現實-它是未知事物的列表。 scala
您可使用TypeTags來作到這一點(正如Daniel已經提到的那樣,但我會明確地將其拼寫出來): code
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): htm
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
不是解決方案,而是一種解決方案,而不用@unchecked
:添加@unchecked
註釋。 看到這裏-http: //www.scala-lang.org/api/current/index.html#scala.unchecked
使用模式匹配防禦
list match { case x:List if x.isInstanceOf(List[String]) => do sth case x:List if x.isInstanceOf(List[Int]) => do sth else }