Les réflexions d'exécution Scala obtiennent tous les membres d'un type spécifique, même pour les classes internes
Avec scala 2.12.10
Supposons que je veuille convertir implicitement au moment de l'exécution une classe de cas, dans ce cas Special
en une classe de cas SpecialString
. La conversion implicite est fournie par un trait External
. Le nom de SpecialString
doit être le nom de déclaration de la classe Special
.
import scala.reflect.runtime.universe.{runtimeMirror, typeOf}
import scala.reflect.runtime.universe
case class Special(i: Int)
case class SpecialString(s: String)
trait External {
val rm = runtimeMirror(getClass.getClassLoader)
val im = rm.reflect(this)
val members = im.symbol.typeSignature.members
def specials: Iterable[universe.Symbol] = members.filter(_.typeSignature <:< typeOf[Special] )
implicit def SpecialIntToString(s: Special): SpecialString = {
val name = im.reflectField(specials.filter(x => im.reflectField(x.asTerm).get.asInstanceOf[Special] == s).head.asTerm).symbol.toString.replace("value ", "")
SpecialString(s"name = $name")
}
}
Actuellement, je suis capable de convertir implicitement les membres convertis déclarés dans une classe étendant le External
trait.
class MyClass extends External {
val firstSpecial = Special(1)
val two = 2
val specialS: SpecialString = firstSpecial
}
class MySecondClass extends MyClass {
val specialS2: SpecialString = firstSpecial
}
val myClass = new MyClass
print(myClass.specialS) // SpecialString(name = firstSpecial)
Mais je ne parviens pas à convertir les membres déclarés dans une super classe
class MyClass {
val firstSpecial = Special(1)
val two = 2
val specialS: SpecialString = firstSpecial
}
class MySecondClass extends MyClass with External {
val specialS2: SpecialString = firstSpecial
}
val myClass = new MyClass
print(myClass.specialS)
val mySecondClass = new MySecondClass
print(mySecondClass.specialS2) // java.util.NoSuchElementException: next on empty iterator
De l'aide?
Réponses
Si vous trouvez le membre nécessaire par son nom plutôt que typeSignature
(et il est effectivement trouvé alors) et imprimez specials.head.typeSignature
et typeOf[Special]
vous verrez pourquoi l'un n'est pas un sous-type d'un autre
trait External {
...
def specials: Iterable[universe.Symbol] =
members.filter(_.name == universe.TermName("firstSpecial") )
//members.filter(_.typeSignature.resultType <:< typeOf[Special] )
println(s"specials.head.typeSignature=${specials.head.typeSignature}=${universe.showRaw(specials.head.typeSignature)}")
println(s"typeOf[Special]=${typeOf[Special]}=${universe.showRaw(typeOf[Special])}")
println(s"specials.head.typeSignature <:< typeOf[Special]=${specials.head.typeSignature <:< typeOf[Special]}")
...
}
//specials.head.typeSignature=pckg.App.Special=NullaryMethodType(TypeRef(ThisType(pckg.App), pckg.App.Special, List()))
//typeOf[Special] =pckg.App.Special=TypeRef(ThisType(pckg.App), pckg.App.Special, List())
//specials.head.typeSignature <:< typeOf[Special]=false
Le type d'une méthode nullaire retournant Special
n'est pas un sous-type de Special
.
Vous devriez ajouter resultType
. Remplacer
trait External {
...
def specials: Iterable[universe.Symbol] =
members.filter(_.typeSignature <:< typeOf[Special] )
avec
trait External {
...
def specials: Iterable[universe.Symbol] =
members.filter(_.typeSignature.resultType <:< typeOf[Special])
Comment trouver le type de données de paramètre de classe au moment de l'exécution dans scala