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

如何在Scala中递归打印一个类的所有子类?

如何在Scala中递归打印一个类的所有子类?

问题背景

我在使用CDE库做类参数化,项目里有大量继承自Config类的子类(示例如下),用户需要手动在Makefile里指定这些类名,我想实现一个工具自动打印所有Config子类的名称,替代grep查找。

class Cfg0 extends Config(...)
class Cfg1 extends Cfg0
class Cfg2 extends Cfg1

解决方案

方法一:运行时反射实现

利用Scala反射API递归遍历已加载的类,收集所有直接/间接继承自Config的子类:

1. 导入反射依赖

import scala.reflect.runtime.universe._
import scala.reflect.runtime.{currentMirror => cm}

2. 递归查找子类的工具函数

def findAllSubclasses(targetType: Type): Set[String] = {
  // 获取当前类的所有直接子类
  val directSubclasses = targetType.typeSymbol.asClass.knownDirectSubclasses
    .filter(_.isClass)
    .map(_.asClass)
    .flatMap { cls =>
      // 确保类已被JVM加载,忽略加载失败的类
      try {
        Some(cm.classSymbol(cm.runtimeClass(cls)).toType)
      } catch {
        case _: ClassNotFoundException => None
      }
    }

  // 递归查找子类的子类,合并当前子类和递归结果
  directSubclasses.flatMap(findAllSubclasses) ++ directSubclasses.map(_.typeSymbol.fullName)
}

3. 调用并输出结果

// 定位Config类的Type
val configType = typeOf[Config]
// 收集所有子类名称
val allConfigSubclasses = findAllSubclasses(configType)
// 排序后打印
println("所有Config子类:")
allConfigSubclasses.toList.sorted.foreach(println)

注意事项

  • 类加载限制:反射只能找到已被JVM加载的类,如果某些子类未被代码引用导致未加载,需要额外实现类路径扫描逻辑来加载这些类。
  • 去重处理:用Set存储类名自动避免重复统计。
  • 适配普通类:虽然参考资料多针对trait/case类,但knownDirectSubclasses对普通类同样有效,递归逻辑可以覆盖所有间接继承的子类。

方法二:编译期宏实现(更可靠)

如果反射无法覆盖未加载的类,可以用Scala宏在编译期扫描所有Config子类:

1. 实现宏注解

import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.reflect.macros.blackbox

class collectConfigSubclasses extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro CollectConfigSubclassesMacro.impl
}

object CollectConfigSubclassesMacro {
  def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
    import c.universe._
    // 定位Config类符号
    val configSymbol = c.typeSymbolOf[Config]
    // 扫描所有直接子类并收集全限定名
    val subclasses = configSymbol.asClass.knownDirectSubclasses
      .filter(_.isClass)
      .map(_.fullName)
      .toList

    // 生成包含子类信息的打印代码
    q"""
      object ConfigSubclassPrinter {
        def printAll(): Unit = {
          println("所有Config子类:")
          ${subclasses}.sorted.foreach(println)
        }
      }
      ${annottees.head}
    """
  }
}

2. 使用宏注解

在任意对象上添加注解,编译后即可调用打印:

@collectConfigSubclasses
object ConfigTools

运行时调用:

ConfigTools.ConfigSubclassPrinter.printAll()

内容的提问来源于stack exchange,提问作者Abraham G

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 00:05:41