Comment trouver le type de données de paramètre de classe au moment de l'exécution dans 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")
}

Incapable de comprendre pourquoi le résultat est Absurdau lieu de I am int?

Mon objectif est de connaître le type de données du paramètre de classe au moment de l'exécution et de le faire correspondre à des types prédéfinis.

Réponses

4 DmytroMitin Nov 29 2020 at 17:20

Les deux sont à jour dataTypeet typeOf[Int]sont imprimés comme Intmais si vous le faites, showRawvous verrez pourquoi ils ne correspondent pas

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

Le fait est que seuls le type Intet le type de méthode nullaire retournant Intsont des types différents.

Essayez d'ajouter .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

Il convient également de mentionner que .decl(TermName("id"))renvoie le symbole getter, c'est .decl(TermName("id "))(avec un espace vide) qui renvoie le symbole de champ. Donc, vous pouvez également le faire avec un espace vide dans le nom du symbole et sans.resultType

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

Je vais ajouter à @TomerShetah de réponse que si l'objectif est tous les champs d'une classe de cas « pattern matching » , alors cela peut être fait aussi au moment de la compilation ( la plupart du temps) avec 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

La réponse @Dmytrio est une excellente explication pour laquelle la réflexion n'a pas fonctionné comme prévu.

D'après votre question, je peux comprendre que ce que vous essayez de faire, c'est en fait une correspondance de modèle avec toutes les variables que vous avez dans une classe de cas. Veuillez envisager de le faire de la manière suivante:

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 exécuté chez Scastie .