Scala-Laufzeitreflexionen erhalten alle Mitglieder eines bestimmten Typs auch für innere Klassen
Mit Scala 2.12.10
Angenommen, ich möchte zur Laufzeit implizit eine Fallklasse konvertieren, in diesem Fall Special
in eine Fallklasse SpecialString
. Die implizite Konvertierung wird durch ein Merkmal bereitgestellt External
. Der Name für SpecialString
sollte der Deklarationsname der Klasse sein 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")
}
}
Derzeit kann ich Konvertierungsmitglieder implizit konvertieren, die in einer Klasse deklariert sind, die das External
Merkmal erweitert.
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)
Aber ich kann keine Mitglieder konvertieren, die in einer Superklasse deklariert sind
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
Irgendeine Hilfe?
Antworten
Wenn Sie Mitglied notwendig namentlich finden statt typeSignature
(und es ist tatsächlich festgestellt , dann) und drucken specials.head.typeSignature
und typeOf[Special]
Sie werden sehen , warum man nicht ein Subtyp eines anderen ist
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
Der Typ einer zurückgegebenen Nullmethode Special
ist kein Subtyp von Special
.
Sie sollten hinzufügen resultType
. Ersetzen
trait External {
...
def specials: Iterable[universe.Symbol] =
members.filter(_.typeSignature <:< typeOf[Special] )
mit
trait External {
...
def specials: Iterable[universe.Symbol] =
members.filter(_.typeSignature.resultType <:< typeOf[Special])
So finden Sie den Datentyp für Klassenparameter zur Laufzeit in Scala