如何解決Scala上的類型擦除? 或者,爲何我沒法獲取集合的類型參數?

在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


#1樓

該答案使用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


#2樓

因爲Java不知道實際的元素類型,所以我發現僅使用List[_]最爲有用。 而後警告消失,代碼描述了現實-它是未知事物的列表。 scala


#3樓

您可使用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


#4樓

不是解決方案,而是一種解決方案,而不用@unchecked :添加@unchecked註釋。 看到這裏-http: //www.scala-lang.org/api/current/index.html#scala.unchecked


#5樓

使用模式匹配防禦

list match  {
        case x:List if x.isInstanceOf(List[String]) => do sth
        case x:List if x.isInstanceOf(List[Int]) => do sth else
     }
相關文章
相關標籤/搜索