Scala嵌套模式匹配引发类型不匹配错误的问题咨询
嘿,这个问题其实是Scala模式匹配语法里的一个常见小坑,我来给你拆解清楚:
问题出在哪?
先看你报错的那行代码:case Not(child: InSubquery) => true,这里的写法被Scala误解了。
在Scala的模式匹配规则里,当你在嵌套的case class构造器里写Var: Type时,它不会被当成「检查Var的类型是Type」,反而会把整个child: InSubquery当成一个变量名——也就是说,代码会把Not的子节点绑定到一个名叫child: InSubquery的变量上,完全没做类型检查。这就导致这个case分支的返回值实际上是该变量的类型(Expression的子类),整个匹配函数的返回类型变成了AnyVal,和partition要求的Expression => Boolean完全不匹配,所以编译器才会抛出类型错误。
结合你的Not定义:case class Not(child: Expression),我们真正需要的是检查这个child是否是InSubquery的实例。
正确的写法有两种
根据你是否需要后续使用这个child变量,有两种解决方式:
1. 不需要绑定child变量(仅做类型检查)
直接用通配符加类型匹配即可:
val (subqueryPredicates, nonSubqueryPredicates) = condition.partition( _ match { case _: InSubquery => true case Not(_: InSubquery) => true // 用_: Type做类型检查,无需变量绑定 case _ => false } )
2. 需要绑定child变量(后续要操作子节点)
如果之后需要用到这个InSubquery类型的child,就用@符号同时实现变量绑定和类型检查:
val (subqueryPredicates, nonSubqueryPredicates) = condition.partition( _ match { case _: InSubquery => true case Not(child @ _: InSubquery) => true // @符号绑定变量+类型检查 case _ => false } )
为什么去掉类型标注就不报错?
当你写成case Not(child) => true时,Scala会把child当成一个普通变量(绑定Not的子节点),但此时没有任何类型检查,整个分支的返回值是true(Boolean类型),所以整个匹配函数的返回类型是Boolean,符合partition的要求,自然就不报错了——不过这种写法其实没达到你想要的「匹配Not包裹的InSubquery」的效果,它会匹配所有Not实例,不管child是什么类型。
总结
Scala模式匹配里,嵌套case class的类型检查不能直接用Class(Var: Type)的写法,必须用Class(_: Type)(无变量绑定)或Class(Var @ _: Type)(有变量绑定),这样才能正确触发类型检查,同时保证返回类型符合预期。
内容的提问来源于stack exchange,提问作者Bostonian

