[FIXED] TypeTag[A] aus Klasse[A] abrufen

Ausgabe

Ich habe eine createOldMethode, 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 createNewvon aus anruft createOld. Mein aktuelles Verständnis ist, dass der Compiler nicht genügend Typinformationen über die Methode Ain createOldhat, 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 TypeTagaus einer ClassScala-Reflexion zu erstellen, obwohl ich mir nicht sicher bin, ob diese Implementierung von TypeCreatorabsolut 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 ClassTags 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 createNewvon createOldist viel einfacher als das mit TypeTags:

def createOld[A](c: Class[A]): A = createNew(ClassTag[A](c))


Beantwortet von –
Vladimir Matveev


Antwort geprüft von –
Mary Flores (FixError Volunteer)

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like