Scala 3能否按泛型类型做模式匹配?实现union入参orElse方法出错
问题根因
- 你的实现核心问题是JVM泛型擦除:代码中
A、A1是泛型参数,在运行时会被抹除为Object类型,你写的inline match中的case _: A实际等价于匹配任意非空值,所有输入都会命中第一个分支,触发错误的强制类型转换。 transparent inline在这里没有起到预期作用,因为orElse本身是泛型方法,编译orElse方法时A和A1的具体类型并不确定,inline展开也没法拿到具体类型做判断,类型检查只能延迟到运行时,自然受擦除影响。
实现方案
该需求完全可以实现,只需要用Scala提供的ClassTag保留泛型参数的运行时类型信息即可。
修正后完整代码
import scala.reflect.ClassTag trait F[A, B] extends (A => B) { // 存储入参类型的运行时标签 def aTag: ClassTag[A] def orElse[A1 : ClassTag, B1](other: F[A1, B1]): F[A | A1, B | B1] = { val self = this val tagA = this.aTag val tagA1 = summon[ClassTag[A1]] new F[A | A1, B | B1] { override val aTag: ClassTag[A | A1] = ClassTag((tagA.runtimeClass || tagA1.runtimeClass).asInstanceOf[Class[A | A1]]) override def apply(v: A | A1): B | B1 = v match { // 用ClassTag做运行时类型匹配,避免擦除问题 case tagA(a) => self(a) case tagA1(a1) => other(a1) } } } } // 提供便捷的构造方法,自动捕获ClassTag object F { def apply[A : ClassTag, B](f: A => B): F[A, B] = new F[A, B] { override val aTag: ClassTag[A] = summon[ClassTag[A]] override def apply(v: A): B = f(v) } }
测试代码
val f1 = F[Int, String](v => s"Int => String : $v") val f2 = F[String, Int](v => v.length) val f = f1.orElse(f2) println(f(42)) // 输出:Int => String : 42 println(f("hello")) // 输出:5
注意事项
上述实现针对基础类型、无泛型的自定义类型完全可用。如果你的入参类型是带泛型的类型(比如List[String]),ClassTag只能识别到擦除后的原生类型(List),无法判断泛型参数,这种场景需要使用编译时的Type信息做匹配,逻辑会更复杂。
内容的提问来源于stack exchange,提问作者wlad031
相关产品推荐
相关产品推荐

