scala 当在扩展特征的伴随中定义时,反射看不到子类CASE类的伴随

zzoitvuj  于 2022-11-09  发布在  Scala
关注(0)|答案(1)|浏览(160)

给定的代码接受一个类型,接受它已知的直接子类,过滤属于Case类的子类,然后接受该Case类的同伴:

def firstSubclassWithCompanion[T: TypeTag]: String = {
  val superclass = implicitly[TypeTag[T]].tpe.typeSymbol.asClass
  val caseClass = superclass.knownDirectSubclasses.map(_.asClass).filter(_.isCaseClass).head
  s"case class $caseClass has companion ${caseClass.companion}"
}

举一个简单的例子

sealed trait With
case class WithCase() extends With

它给出了预期收益

> firstSubclassWithCompanion[With]
"class WithCase has companion object WithCase"

因为With特征有一个WithCase子类,这个子类是Case类,它有一个伴随对象(由编译器定义)。
但是,在下面的示例中,子类是在继承特征的伴随对象中定义的:

sealed trait Without
object Without {
  case class WithoutCase() extends Without
}

它不返回伴随对象

> firstSubclassWithCompanion[Without]
"class WithoutCase has companion <none>"

如果在其他对象中定义它,则可以很好地工作。

sczxawaw

sczxawaw1#

错误应在https://github.com/scala/bug/issues报告
解决方法是使用caseClass.owner.typeSignature.decl(caseClass.name)而不是caseClass.companion
另一种解决方法是将此运行时反射代码转换为宏(编译时反射)。因为这里的所有类都是在编译时定义的,所以使用宏是有意义的。

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

def firstSubclassWithCompanion[T]: String = macro firstSubclassWithCompanionImpl[T]

def firstSubclassWithCompanionImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
  import c.universe._
  val superclass = weakTypeOf[T].typeSymbol.asClass
  val caseClass = superclass.knownDirectSubclasses.map(_.asClass).filter(_.isCaseClass).head
  val res = s"case class $caseClass has companion ${caseClass.companion}"
  q"$res"
}

相关问题