UPDATE:在結束時加入的替代方案。
由於類型擦除,您在遇到泛型類型時遇到了模式匹配的侷限性。
然而,所有都不會丟失。我們可以依靠ClassManifests實現一個通用的方法,你的類轉換到目標類型T(以及其他類似的上轉換成元[T]):
trait Meta[T] { this: T =>
type t = T
def metaManifest: ClassManifest[T]
def ~=(e: T): Boolean
}
abstract sealed class Base {
def as[T:ClassManifest]: Option[T] = {
if (classManifest[T].erasure.isAssignableFrom(this.getClass)) Some(this.asInstanceOf[T])
else None
}
def asMeta[T:ClassManifest]: Option[T with Meta[T]] = {
this match {
case meta: Meta[_] if classManifest[T] <:< meta.metaManifest => as[T].asInstanceOf[Option[T with Meta[T]]]
case _ => None
}
}
}
abstract sealed class A extends Base
case class Ide(s: String) extends A
case class MIde(s: String) extends A with Meta[A] {
val metaManifest = classManifest[A]
def ~=(e: A) = e match {
case e: Ide => true
case e: MIde => false
}
}
sealed abstract class B extends Base
case class Foo(s: String) extends B
讓我們來測試這個在REPL:
scala> m.as[A]
res17: Option[A] = Some(MIde(x))
scala> m.asMeta[A]
res18: Option[A with Meta[A]] = Some(MIde(x))
scala> i.as[A]
res19: Option[A] = Some(Ide(i))
scala> i.asMeta[A]
res20: Option[A with Meta[A]] = None
scala> f.as[A]
res21: Option[A] = None
scala> f.asMeta[A]
res22: Option[A with Meta[A]] = None
聽起來不錯。現在,我們可以重寫我們的模式與此匹配:
(m, i) match {
case (x: Meta[T], y: T) if x ~= y => println("right")
case _ => println("wrong")
}
這樣:
(m.asMeta[T], i.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
所以,你比如現在應該是這樣的:
object Test {
val m = MIde("x")
val i = Ide("i")
val f = Foo("f")
def test[T:ClassManifest]() {
(m.asMeta[T], i.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
// -> right
(m.asMeta[T], f.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
}
}
更新:如果設置顯式metaManifest
每次你混合使用Meta
不是一個選項,你可以讓scala自動推斷它,通過將其隱含在Meta
的構造函數中。這意味着Meta
現在必須是一個類,因此A
和B
(以及必須顯示爲Meta
的類型參數的所有類似類型)現在必須是特徵,因爲您不能混合2個類。所以你基本上換了另一個限制。選擇你最喜歡的一個。 這裏,我們去:
abstract sealed class Meta[T](implicit val metaManifest: ClassManifest[T]) { this: T =>
type t = T
def ~=(e: T): Boolean
}
trait Base {
def as[T:ClassManifest]: Option[T] = {
if (classManifest[T].erasure.isAssignableFrom(this.getClass)) Some(this.asInstanceOf[T])
else None
}
def asMeta[T:ClassManifest]: Option[T with Meta[T]] = {
this match {
case meta: Meta[_] if classManifest[T] != ClassManifest.Nothing && classManifest[T] <:< meta.metaManifest => as[T].asInstanceOf[Option[T with Meta[T]]]
case _ => None
}
}
}
trait A extends Base
case class Ide(s: String) extends A
case class MIde(s: String) extends Meta[A] with A {
def ~=(e: A) = e match {
case e: Ide => true
case e: MIde => false
}
}
trait B extends Base
case class Foo(s: String) extends B
object Test {
val m = MIde("x")
val i = Ide("i")
val f = Foo("f")
def test[T:ClassManifest]() {
(m.asMeta[T], i.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
(m.asMeta[T], f.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
}
}
最後,如果既沒有解決方案適合你,你可以嘗試另一個問題:不是混合Meta[T]
與T
,只是包裹。 Meta[T]
然後只是包裝到T
,你甚至可以添加從Meta[T]
到其包裝值的隱式轉換,以便Meta[T]
的實例可以像T
的實例幾乎透明地一樣有效地使用。
感謝您的答案,但不幸的是,即使使用您的解決方案,我仍然得到ClassCaseException。 – schlicht
上面的代碼適用於我。你能詳細說明哪些代碼完全不起作用嗎?你在做什麼比在我的代碼片段不同? –
我粘貼整個代碼片段注入斯卡拉解釋,然後我跑Test.test(): Test.test() 權 java.lang.ClassCastException:富不能轉換爲 – schlicht