1. 问题的引出:内部类中模式匹配出错
trait A {
case class B(i: Int)
}
class C extends A {
def !(a: Any) = a match{
case B(0) => println("never be here") // 未匹配到:这里的B(0)类型为C#B和
case b: B => println("never be here2") // 未匹配到:这里的B类型为C#B
case B => println("never be here3") // 未匹配到:这里的B类型为C#B
case x => println(s"received $x") // 匹配:匹配任意情形
}
}
class D extends A{
new C ! B(0) // 这里的B(0)类型为D#B
}
2. 路径依赖导致的模式匹配问题
2.1 问题1:
两个不同子类分别实例化继承的内部case类,将会产生两个不同的对象,路径依赖导致无法模式匹配
// case类自动实现了equals方法,在进行模式匹配时,equals主要对比构造函数的参数是否相等
case class X(i: Int)
new X(0) == new X(0) // true
// 对内部类的case类而言,需要考虑路径依赖问题
trait A{case class X(i: Int)}
class C extends A
class D extends A
val c = new C
val d = new D
val x1 = new c.X(0) // 类型为c.X
val x2 = new d.X(0) // 类型为d.X
val x3 = new d.X(0) // 类型为d.X
x1 == x2 // false:不同的外部对象,对应的内部case类是不同的
x2 == x3 // true:路径依赖相同
2.2 问题2:
case类自动生成伴生对象,两个不同子类中继承的case类的伴生对象,是两个不同的对象,路径依赖导致无法模式匹配
trait A{case class B(i: Int)}
class C extends A
class D extends A
val b1 = (new C).B // 类型为C#B
val b2 = (new D).B // 类型为D#B
b1 == b2 // false:不同的外部,对应的内部对象是不同的
3. 解决方案
3.1 解决方案1:
内部case类的实例化和使用放在同一上下文
trait A{case class B(i: Int)}
class C extends A{
def foo(a: Any) = a match {
case B(0) => println("never be here")
case b: B => println("never be here2")
caes B => println("never be here3")
case x => println("received")
}
foo(B(0)) // 参数中的B(0)和模式匹配中的B(0)、b:B、B处于统一上下文,类型一致,可以匹配
}
3.2 解决方案2:
使用类型投影进行匹配[外部类]#[内部类]#[内部类的内部类]…
trait A{case class B(i: Int)}
class C extends A {
def !(a: Any) = a match{
case b: C#B => println("C#B") // 可以匹配
case b: D#B => println("D#B") // 可以匹配
case b: A#B => println("A#B") // 可以匹配
case _ => println("-1")
}
}
3.3 解决方案3:
使用同一对象通过new关键字实例化case类B,调用同一对象的!方法进行模式匹配
trait A{case class B(i: Int)}
class C extends A{
def !(a:Any) = a match {
case B(0) => println("match B(0)")
case _ => println("-1")
}
}
class D extends A{
val c = new C
val b = new c.B(0) // 根据路径依赖,这里的b的类型为c.B
c ! b // 根据路径依赖此时c的!函数中的B(0)也为c.B类型,两个B为同一路径,可以完成匹配
// new C ! b不能匹配,因为new C产生的对象与c不是同一对象,因此与b路径依赖不同,无法匹配
}
new D