获取一个类的子类(们)的递归方法

huangapple go评论74阅读模式
英文:

Recursively obtain subclass(es) of a class

问题

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

Context:

我正在使用CDE库(https://github.com/chipsalliance/cde),该库在各种下游项目中用于参数化类。由于使用该库,会创建大量的案例类,这些案例类扩展了一个名为“Config”的类(分布在多个Scala文件中),如下所示:

class Cfg0 extends Config(...)

class Cfg1 extends Cfg0

class Cfg2 extends Cfg1

我们的用户通常会在Makefiles等地方手动指定“Config”对象的名称,我想打印所有类的名称,以便更容易发现这些类(而不是使用grep)。

“Config”类的定义:https://github.com/chipsalliance/cde/blob/384c06b8d45c8184ca2f3fba2f8e78f79d2c1b51/cde/src/chipsalliance/rocketchip/config.scala#L151

我已经查看了以下链接:

https://stackoverflow.com/questions/55834824/how-do-i-use-scala-reflection-to-find-all-subclasses-of-a-trait-without-using-t
https://gist.github.com/longshorej/1a0a2cf50de8e6ff101c

但它们似乎是面向案例类/特征的。

英文:

Main Question: How can you print all the subclasses (recursively) of a class in Scala?

Context:

I am using the CDE library (https://github.com/chipsalliance/cde) that is used in a variety of downstream projects to parameterize classes. As a result of using the library, a large amount of case classes are created extending a class called Config (across multiple Scala files), like so:

class Cfg0 extends Config(...)

class Cfg1 extends Cfg0

class Cfg2 extends Cfg1

Our users normally manually call out the "Config" object names in Makefiles, etc and I would like to print the names of all the classes so that these classes are easier to discover (instead of grepping).

Definition of Config class: https://github.com/chipsalliance/cde/blob/384c06b8d45c8184ca2f3fba2f8e78f79d2c1b51/cde/src/chipsalliance/rocketchip/config.scala#L151

I've looked at:

https://stackoverflow.com/questions/55834824/how-do-i-use-scala-reflection-to-find-all-subclasses-of-a-trait-without-using-t
https://gist.github.com/longshorej/1a0a2cf50de8e6ff101c

But they looked like they were oriented around case classes/traits.

答案1

得分: 0

I slightly updated my answer at https://stackoverflow.com/questions/55834824/how-do-i-use-scala-reflection-to-find-all-subclasses-of-a trait without using t

Out of the options listed there, for example at runtime you can use classpath scanners like Reflections, ClassGraph, Burningwave, ...

// libraryDependencies += "org.reflections" % "reflections" % "0.10.2"
import org.reflections.Reflections
import org.reflections.scanners.Scanners.SubTypes
import scala.jdk.CollectionConverters._

val reflections = new Reflections()
reflections.get(SubTypes.of(classOf[Config]).asClass()).asScala
// Set(class Cfg0, class Cfg1, class Cfg2)

Or at compile time you can try Shapeless and macro-based type class KnownSubclasses from https://stackoverflow.com/questions/75544693/shapeless-how-to-derive-labelledgeneric-for-coproduct (traversing ASTs in all compilation units of a current compiler run, not using scala-reflect .knownDirectSubclasses)

// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.{Coproduct, HList, Poly0, Poly1, Typeable}
import shapeless.ops.coproduct.ToHList
import shapeless.ops.hlist.{FillWith, Mapper, ToList}

object typeablePoly extends Poly1 {
  implicit def cse[A](implicit typeable: Typeable[A]): Case.Aux[A, String] =
    at(_ => typeable.describe)
}

object nullPoly extends Poly0 {
  implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}

def getSubclasses[A] = new PartiallyAppliedGetSubclasses[A]

class PartiallyAppliedGetSubclasses[A] {
  def apply[C <: Coproduct, L <: HList, L1 <: HList]()(implicit
    knownSubclasses: KnownSubclasses.Aux[A, C],
    toHList: ToHList.Aux[C, L],
    mapper: Mapper.Aux[typeablePoly.type, L, L1],
    fillWith: FillWith[nullPoly.type, L],
    toList: ToList[L1, String]
  ): List[String] =
    toList(mapper(fillWith()))
}

getSubclasses[Config]()
// List(Cfg0, Cfg1, Cfg2)

For both above options it's irrelevant whether traits/classes are sealed or not, whether classes are case classes or not.

英文:

I slightly updated my answer at https://stackoverflow.com/questions/55834824/how-do-i-use-scala-reflection-to-find-all-subclasses-of-a-trait-without-using-t

Out of the options listed there, for example at runtime you can use classpath scanners like Reflections, ClassGraph, Burningwave, ...

// libraryDependencies += "org.reflections" % "reflections" % "0.10.2"
import org.reflections.Reflections
import org.reflections.scanners.Scanners.SubTypes
import scala.jdk.CollectionConverters._

val reflections = new Reflections()
reflections.get(SubTypes.of(classOf[Config]).asClass()).asScala
// Set(class Cfg0, class Cfg1, class Cfg2)

Or at compile time you can try Shapeless and macro-based type class KnownSubclasses from https://stackoverflow.com/questions/75544693/shapeless-how-to-derive-labelledgeneric-for-coproduct (traversing ASTs in all compilation units of a current compiler run, not using scala-reflect .knownDirectSubclasses)

// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.{Coproduct, HList, Poly0, Poly1, Typeable}
import shapeless.ops.coproduct.ToHList
import shapeless.ops.hlist.{FillWith, Mapper, ToList}

object typeablePoly extends Poly1 {
  implicit def cse[A](implicit typeable: Typeable[A]): Case.Aux[A, String] =
    at(_ => typeable.describe)
}

object nullPoly extends Poly0 {
  implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}

def getSubclasses[A] = new PartiallyAppliedGetSubclasses[A]

class PartiallyAppliedGetSubclasses[A] {
  def apply[C <: Coproduct, L <: HList, L1 <: HList]()(implicit
    knownSubclasses: KnownSubclasses.Aux[A, C],
    toHList: ToHList.Aux[C, L],
    mapper: Mapper.Aux[typeablePoly.type, L, L1],
    fillWith: FillWith[nullPoly.type, L],
    toList: ToList[L1, String]
  ): List[String] =
    toList(mapper(fillWith()))
}

getSubclasses[Config]()
// List(Cfg0, Cfg1, Cfg2)

For both above options it's irrelevant whether traits/classes are sealed or not, whether classes are case classes or not.

huangapple
  • 本文由 发表于 2023年4月4日 06:43:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/75924247.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定