Scala-Laufzeitreflexionen erhalten alle Mitglieder eines bestimmten Typs auch für innere Klassen

Dec 16 2020

Mit Scala 2.12.10

Angenommen, ich möchte zur Laufzeit implizit eine Fallklasse konvertieren, in diesem Fall Specialin eine Fallklasse SpecialString. Die implizite Konvertierung wird durch ein Merkmal bereitgestellt External. Der Name für SpecialStringsollte 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 ExternalMerkmal 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

1 DmytroMitin Dec 17 2020 at 11:51

Wenn Sie Mitglied notwendig namentlich finden statt typeSignature(und es ist tatsächlich festgestellt , dann) und drucken specials.head.typeSignatureund 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 Specialist 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