如何在Dotty中使用通配符?附Scala示例编译问题
解决Scala 3(Dotty)中遍历Case Class List字段的通配符问题
首先来看你提供的原代码(在Scala 2中可运行,但Scala 3/Dotty无法编译):
case class ListTest( listString: List[String], listInt: List[Int] ) { def map[_,O](ff: List[_] => O): Iterable[O] = { getClass.getDeclaredFields.map { field => field.get(this) match { case list: List[_] => Some(ff(list)) case _ => None } }.flatMap(x => x) } } val lists = ListTest(List("asdf", "1234"), List(1,2,...
原代码在Scala 3中编译失败的原因
- 未命名类型参数非法:Scala 3不再允许用
_作为未命名的类型参数,原方法map[_,O]里的第一个_会直接触发编译错误,类型参数必须有明确名称。 - 反射与类型擦除问题:Scala 3对类型检查的严格性提升,原代码中
case list: List[_]的模式匹配会因类型擦除出现警告甚至编译失败;同时case class的字段默认是私有访问,直接调用field.get(this)可能抛出权限异常。 - 冗余类型参数:原方法的第一个类型参数
_没有实际用途,属于无效定义。
修正后的Scala 3兼容代码
方案1:保留反射方式(调整类型与权限处理)
import java.lang.reflect.Field case class ListTest(listString: List[String], listInt: List[Int]) { // 移除无用类型参数,用Scala 3的List[?]替代List[_](语义等价) def map[O](ff: List[?] => O): Iterable[O] = { getClass.getDeclaredFields // 过滤case class自动生成的合成字段(如productPrefix、productArity等) .filterNot(_.isSynthetic) .flatMap { (field: Field) => field.setAccessible(true) // 允许访问私有字段 field.get(this) match { case list: List[?] => Some(ff(list)) case _ => None } } .toIterable } } // 测试代码 val lists = ListTest(List("asdf", "1234"), List(1, 2, 3, 4)) val sizeResult = lists.map(_.size) println(sizeResult) // 输出: Iterable(2, 4)
方案2:更安全的Product遍历(无需反射)
所有Scala case class都继承自Product特质,我们可以利用productIterator直接遍历字段值,完全避开反射的坑,这也是Scala 3中更推荐的写法:
case class ListTest(listString: List[String], listInt: List[Int]) { def map[O](ff: List[?] => O): Iterable[O] = { productIterator .flatMap { case list: List[?] => Some(ff(list)) case _ => None } .toIterable } } // 测试代码 val lists = ListTest(List("asdf", "1234"), List(1, 2, 3, 4)) val joinedResult = lists.map(_.mkString(" | ")) println(joinedResult) // 输出: Iterable("asdf | 1234", "1 | 2 | 3 | 4")
关键修正点说明
- 移除了无效的未命名类型参数,简化了方法签名;
- 用
List[?]替代Scala 2风格的List[_],这是Scala 3中统一的通配符写法; - 方案2利用Product特性避免反射,代码更简洁、类型更安全,同时完全兼容Scala 3的编译规则。
内容的提问来源于stack exchange,提问作者bbarker
相关产品推荐
相关产品推荐

