Ausgabe
Ich habe eine createOld
Methode, die ich überschreiben muss, und ich kann sie nicht ändern. Ich möchte verwenden TypeTag
, um Musterabgleich bereitzustellen, geben Sie ein createNew
. Das Ziel ist herauszufinden, wie man createNew
von aus anruft createOld
. Mein aktuelles Verständnis ist, dass der Compiler nicht genügend Typinformationen über die Methode A
in createOld
hat, wenn er nicht bereits mit kommt TypeTag[A]
.
object TypeTagFromClass {
class C1
class C2
// How to get TypeTag[A] needed by createNew?
def createOld[A](c: Class[A]): A = createNew ???
def createNew[A : TypeTag]: A = {
val result = typeOf[A] match {
case a if a =:= typeOf[C1] => new C1()
case a if a =:= typeOf[C2] => new C2()
}
result.asInstanceOf[A]
}
}
Lösung
Es ist möglich, eine TypeTag
aus einer Class
Scala-Reflexion zu erstellen, obwohl ich mir nicht sicher bin, ob diese Implementierung von TypeCreator
absolut korrekt ist:
import scala.reflect.runtime.universe._
def createOld[A](c: Class[A]): A = createNew {
val mirror = runtimeMirror(c.getClassLoader) // obtain runtime mirror
val sym = mirror.staticClass(c.getName) // obtain class symbol for `c`
val tpe = sym.selfType // obtain type object for `c`
// create a type tag which contains above type object
TypeTag(mirror, new TypeCreator {
def apply[U <: Universe with Singleton](m: api.Mirror[U]) =
if (m eq mirror) tpe.asInstanceOf[U # Type]
else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.")
})
}
Sie brauchen jedoch nicht wirklich full TypeTag
, wenn Sie keine generischen Parameter und vollständige Scala-Typinformationen überprüfen müssen. Sie können dafür ClassTag
s verwenden:
def createNew[A: ClassTag]: A = {
val result = classTag[A].runtimeClass match {
case a if a.isAssignableFrom(classOf[C1]) => new C1()
case a if a.isAssignableFrom(classOf[C2]) => new C2()
}
result.asInstanceOf[A]
}
Oder mit etwas implizitem Zucker:
implicit class ClassTagOps[T](val classTag: ClassTag[T]) extends AnyVal {
def <<:(other: ClassTag[_]) = classTag.runtimeClass.isAssignableFrom(other.runtimeClass)
}
def createNew[A: ClassTag]: A = {
val result = classTag[A] match {
case a if a <<: classTag[C1] => new C1()
case a if a <<: classTag[C2] => new C2()
}
result.asInstanceOf[A]
}
Sie können das noch weiter vereinfachen, indem Sie die einfache alte Java- newInstance()
Methode verwenden:
def createNew[A: ClassTag]: A = classTag[A].runtimeClass.newInstance().asInstanceOf[A]
Dies würde natürlich nur funktionieren, wenn Sie keine unterschiedlichen Konstruktorparameter für verschiedene Klassen benötigen.
Das Aufrufen createNew
von createOld
ist viel einfacher als das mit TypeTag
s:
def createOld[A](c: Class[A]): A = createNew(ClassTag[A](c))
Beantwortet von – Vladimir Matveev
Antwort geprüft von – Mary Flores (FixError Volunteer)