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

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]会得到字段类型列表,需求是:

  1. 对列表中自定义类类型(如Bar)递归调用类似f的逻辑,打印其内部字段
  2. 从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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 08:22:19