更新:最后添加了替代方案。
由于类型擦除,您在泛型类型方面遇到了模式匹配的限制。
然而,一切都没有丢失。我们可以依靠 ClassManifests 实现一个泛型方法来将您的类转换为目标类型 T(以及另一个类似的转换为 Meta[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")
}
}
}
更新:如果每次混合 Meta 时都显式设置 metaManifest 不是一个选项,你可以让 scala 通过在 Metas 构造函数中隐式传递它来自动推断它。这意味着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 的实例一样有效地使用透明的。