So finden Sie den Datentyp für Klassenparameter zur Laufzeit in Scala

Nov 29 2020
import scala.reflect.runtime.universe
import scala.reflect.runtime.universe._

def getType[T: TypeTag](obj: T) = typeOf[T]

case class Thing(
  val id: Int,
  var name: String
)
val thing = Thing(1, "Apple")

val dataType = getType(thing).decl(TermName("id")).asTerm.typeSignature

dataType match {
 case t if t =:= typeOf[Int] => println("I am Int")
 case t if t =:= typeOf[String] => println("String, Do some stuff")
 case _ => println("Absurd")
}

Nicht in der Lage zu verdauen, warum Ergebnis Absurdstatt ist I am int?

Mein Ziel ist es, den Datentyp des Klassenparameters zur Laufzeit zu kennen und ihn mit vordefinierten Typen abzugleichen.

Antworten

4 DmytroMitin Nov 29 2020 at 17:20

Beide sind aktuell dataTypeund typeOf[Int]werden so gedruckt, Intaber wenn Sie dies tun, werden showRawSie sehen, warum sie nicht übereinstimmen

showRaw(dataType) // NullaryMethodType(TypeRef(ThisType(scala), scala.Int, List()))
showRaw(typeOf[Int]) // TypeRef(ThisType(scala), scala.Int, List())

Die Sache ist, dass nur der Typ Intund der Typ der zurückgegebenen Nullmethode Intunterschiedliche Typen sind.

Versuchen Sie hinzuzufügen .resultType

val dataType = getType(thing).decl(TermName("id")).asTerm.typeSignature.resultType

dataType match {
  case t if t =:= typeOf[Int] => println("I am Int")
  case t if t =:= typeOf[String] => println("String, Do some stuff")
  case _ => println("Absurd")
} // I am Int

Erwähnenswert ist auch, dass .decl(TermName("id"))das Getter-Symbol .decl(TermName("id "))(mit einem Leerzeichen) das Feldsymbol zurückgibt. Alternativ können Sie also mit und ohne Leerzeichen im Symbolnamen arbeiten.resultType

val dataType = getType(thing).decl(TermName("id ")).asTerm.typeSignature

Ich werde hinzufügen @TomerShetah ‚s Antwort , dass , wenn das Ziel‚Pattern Matching‘ werden alle Felder einer Fallklasse ist , dann kann dies auch (meistens) bei der Kompilierung durchgeführt werden mit Shapeless :

import shapeless.Poly1
import shapeless.syntax.std.product._

object printTypes extends Poly1 {
  implicit val int: Case.Aux[Int, Unit] = at(t => println(s"I am Int: $t")) implicit val string: Case.Aux[String, Unit] = at(t => println(s"String, Do some stuff: $t"))
  implicit def default[V]: Case.Aux[V, Unit] = at(t => println(s"Absurd: $t"))
}
  
thing.toHList.map(printTypes)
// I am Int: 1
// String, Do some stuff: Apple

https://scastie.scala-lang.org/DmytroMitin/N4Idk4KcRumQJZE2CHC0yQ

1 TomerShetah Nov 30 2020 at 11:28

Die Antwort von @Dmytrio ist eine großartige Erklärung dafür, warum die Reflexion nicht wie erwartet funktioniert hat.

Ich kann anhand Ihrer Frage verstehen, dass Sie tatsächlich versuchen, alle Variablen, die Sie in einer Fallklasse haben, mit Mustern abzugleichen. Bitte denken Sie daran, dies folgendermaßen zu tun:

case class Thing(id: Int, name: String)
val thing = Thing(1, "Apple")

thing.productIterator.foreach {
  case t: Int => println(s"I am Int: $t") case t: String => println(s"String, Do some stuff: $t")
  case t => println(s"Absurd: $t")
} 

Code läuft bei Scastie .