You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 03:57:00