Scala case类递归反射问题:如何递归获取字段类型?
Scala反射递归打印自定义类字段及Type转换实现
问题背景
现有两个Scala case类:
case class Bar(x: Int) case class Foo(b: Bar, z: Double)
通过反射获取Foo字段类型的代码:
import scala.reflect.runtime.universe._ def f[T: TypeTag] = typeOf[T].members.filter(!_.isMethod)
调用f[Foo]会得到字段类型列表,需求是:
- 对列表中自定义类类型(如
Bar)递归调用类似f的逻辑,打印其内部字段 - 从
Bar的Type对象获取对应的TypeTag,用于触发递归
解决方案
1. 递归打印字段的实现
我们可以改造原有逻辑,实现一个递归遍历并打印字段的函数,核心是从字段的Type生成对应的TypeTag,进而递归处理自定义类:
import scala.reflect.runtime.universe._ import scala.reflect.runtime.currentMirror case class Bar(x: Int) case class Foo(b: Bar, z: Double) // 递归打印类的字段,支持自定义类嵌套 def printNestedFields[T: TypeTag](indent: String = ""): Unit = { val currentType = typeOf[T] val className = currentType.typeSymbol.name println(s"$indent$className 的字段列表:") // 过滤出类的成员变量(排除方法、构造器等非字段成员) currentType.members.filter(m => m.isTerm && !m.isMethod).foreach { field => val fieldTerm = field.asTerm val fieldType = fieldTerm.typeSignature println(s"$indent - ${fieldTerm.name}: $fieldType") // 判断是否为需要递归的自定义类(排除基本类型、Any/AnyRef等通用类型) val fieldSymbol = fieldType.typeSymbol if (fieldSymbol.isClass) { val fieldClass = fieldSymbol.asClass if (!fieldClass.isPrimitive && fieldType != typeOf[Any] && fieldType != typeOf[AnyRef]) { // 从Type生成对应的TypeTag,用于递归调用 val fieldTypeTag = TypeTag(currentMirror, new TypeCreator { override def apply[U <: Universe with Singleton](mirror: Mirror { type MirrorUniverse = U }): U#Type = fieldType.asInstanceOf[U#Type] }) // 递归打印,增加缩进提升可读性 printNestedFields(fieldTypeTag)(indent + " ") } } } } // 测试调用 printNestedFields[Foo]()
执行后输出:
Foo 的字段列表: - b: Bar Bar 的字段列表: - x: Int - z: Double
2. 从Type获取对应TypeTag的方法
上述代码中已经包含了从Type生成TypeTag的核心逻辑:
val fieldTypeTag = TypeTag(currentMirror, new TypeCreator { override def apply[U <: Universe with Singleton](mirror: Mirror { type MirrorUniverse = U }): U#Type = fieldType.asInstanceOf[U#Type] })
通过TypeTag.apply方法,结合当前运行时镜像currentMirror和自定义的TypeCreator,即可将已有的Type对象转换为对应的TypeTag,满足泛型方法对隐式TypeTag参数的要求。
关键细节说明
- 字段过滤:使用
m.isTerm && !m.isMethod更精准地筛选类的成员变量,避免误判构造器、伴生对象方法等非字段成员 - 递归判断:通过
fieldSymbol.isClass和!fieldClass.isPrimitive排除基本类型,同时过滤Any/AnyRef等通用类型,避免无效递归 - 类型转换:
TypeCreator的apply方法需要做类型转换,因为不同Universe下的Type是不同的子类型,这里通过asInstanceOf完成兼容转换
内容的提问来源于stack exchange,提问作者outlaw
相关产品推荐
相关产品推荐

