如何获取仅含实现类的Constraint trait同类型列表?
嘿,要获取Constraint trait的所有实现类列表,我给你分享几个在Scala里实用的方案,你可以根据项目规模和需求来选:
方案1:手动维护实现类注册表(简单可控)
如果你的项目比较小,实现类不多,手动维护是最直接的方式——自己建一个注册表对象,把所有Constraint的实现类都加进去:
object ConstraintRegistry { // 直接列出所有Constraint的实现类 val allConstraintImpls: List[Class[_ <: Constraint]] = List( classOf[ConstraintA], classOf[ConstraintB] ) }
用的时候直接调用ConstraintRegistry.allConstraintImpls就能拿到列表啦。优点是简单易懂、没有额外依赖,缺点是新增实现类时必须手动更新这个列表,容易忘加。
方案2:运行时反射扫描类路径(自动发现)
如果项目比较大,不想手动维护列表,可以用反射自动扫描指定包下所有继承Constraint的类。这里分两种方式:
原生Scala反射实现
不需要额外依赖,用Scala自带的反射API就能搞定,不过需要指定扫描的包路径:
import scala.reflect.runtime.universe._ object ConstraintScanner { def findAllConstraints: List[Class[_ <: Constraint]] = { val classLoader = getClass.getClassLoader val mirror = runtimeMirror(classLoader) // 替换成你实际存放Constraint实现类的包路径 val targetPackage = mirror.staticPackage("com.yourproject.constraints") val allClassesInPackage = targetPackage.info.decls.filter(_.isClass).map(_.asClass) // 筛选出Constraint的非抽象子类 allClassesInPackage.filter { cls => cls.baseClasses.contains(typeOf[Constraint].typeSymbol) && !cls.isAbstract }.map(mirror.runtimeClass(_).asInstanceOf[Class[_ <: Constraint]]).toList } }
第三方库(Reflections)增强扫描
如果原生反射的能力不够(比如需要扫描多个包、动态加载的类),可以用Reflections库,它的扫描能力更强大。先添加依赖(比如sbt里:libraryDependencies += "org.reflections" % "reflections" % "0.10.2"),然后实现:
import org.reflections.Reflections object ConstraintReflectionsScanner { def findAllConstraints: List[Class[_ <: Constraint]] = { // 指定要扫描的根包路径 val reflections = new Reflections("com.yourproject.constraints") // 获取所有Constraint的子类 reflections.getSubTypesOf(classOf[Constraint]).toList } }
这种方式会自动发现指定包下所有Constraint的实现类,新增类不用手动改代码,非常适合大型项目。
方案3:编译时生成列表(无运行时开销)
如果不想有运行时反射的性能开销,可以用Scala宏在编译时自动生成实现类列表,这样列表在编译阶段就确定了,运行时直接用:
import scala.language.experimental.macros import scala.reflect.macros.blackbox object ConstraintMacro { // 对外暴露的方法,调用时会触发宏展开 def getConstraintImpls: List[Class[_ <: Constraint]] = macro getConstraintImplsImpl // 宏的具体实现 private def getConstraintImplsImpl(c: blackbox.Context): c.Tree = { import c.universe._ val constraintType = typeOf[Constraint] // 扫描当前编译环境下的所有类,筛选出Constraint的非抽象子类 val validClasses = c.universe.rootMirror.decls .filter(_.isClass).map(_.asClass) .filter(cls => cls.baseClasses.contains(constraintType.typeSymbol) && !cls.isAbstract) // 生成List(classOf[ConstraintA], classOf[ConstraintB])这样的代码 val classLiterals = validClasses.map(cls => q"classOf[${cls.toType}]") q"List(..$classLiterals)" } }
使用的时候直接调用ConstraintMacro.getConstraintImpls就可以了,没有运行时反射的开销,但需要你了解Scala宏的基本用法,门槛稍高。
内容的提问来源于stack exchange,提问作者Xavier Silva

