我在 Scala 2 中有一个使用反射的方法。它在 Scala 3 中不起作用,我想重新实现它,以便它适用于 Scala 3。(我不再需要它用于 Scala 2,所以它不需要同时适用于 both)。
该方法的目的是检查一个对象并按照声明的顺序收集其作为成员的单例对象的任何实例。 (程序员提前知道所有此类单例对象都会扩展
A
)。
工作正常。
def allSingletonObjects[A](host: Any): Vector[A] = {
import scala.reflect.runtime.universe.runtimeMirror
val mirror = runtimeMirror(host.getClass.getClassLoader)
mirror
.classSymbol(host.getClass)
.info
.members
.filter(_.isModule)
.map(sym => mirror.reflectModule(sym.asModule).instance.asInstanceOf[A])
.toVector
.reverse
}
这是一个如何使用它的示例。
sealed trait Color
object Color {
case object Red extends Color
case object Green extends Color
case object Blue extends Color
}
在此示例中,
allSingletonObjects[Color](Color)
将返回 Vector(Color.Red, Color.Green, Color.Blue)
。
如何在 Scala 3 中实现
allSingletonObjects
?
注意:我无法更改类型建模的方式(例如使用枚举)。理想情况下,我希望
allSingletonObjects
能够像 Scala 2 中那样工作。
我同意这些评论,你可能应该找到更好的方法来做到这一点,但这是一个工作版本:
import scala.quoted.*
inline def allSingletonObjects[A](host: Any) = allSingletonObjectsSeq[A](host).toVector
inline def allSingletonObjectsSeq[A](host: Any) = ${allSingletonObjectsSeqImpl[A]('host)}
def allSingletonObjectsSeqImpl[A: Type](host: Expr[Any])(using quotes: Quotes): Expr[Seq[A]] =
import quotes.reflect.*
Expr.ofSeq:
host
.asTerm
.tpe
.classSymbol
.get
.declarations
.filter(sym => sym.flags.is(Flags.Case | Flags.Final | Flags.Lazy | Flags.Module | Flags.StableRealizable))
.map(sym => Ref(sym).asExprOf[A])