Jak znaleźć typ danych parametru klasy w czasie wykonywania w scali

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")
}

Nie jesteś w stanie przetrawić, dlaczego wynik jest Absurdzamiast I am int?

Moim celem jest poznanie typu danych parametru klasy w czasie wykonywania i dopasowanie go do predefiniowanych typów.

Odpowiedzi

4 DmytroMitin Nov 29 2020 at 17:20

Zarówno aktualne, jak dataTypei typeOf[Int]są drukowane, Intale jeśli to zrobisz showRaw, zobaczysz, dlaczego nie pasują

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

Chodzi o to, że tylko typ Inti typ zwracanej metody zerowej Intsą różnymi typami.

Spróbuj dodać .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

Warto również wspomnieć, że .decl(TermName("id"))zwraca symbol pobierający, to .decl(TermName("id "))(ze spacją) zwraca symbol pola. Więc alternatywnie możesz zrobić spację w nazwie symbolu i bez.resultType

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

Dodam do @TomerShetah jest odpowiedź , że jeśli celem jest «dopasowanie wzorca» wszystkie pola klasy case następnie można to zrobić również w czasie kompilacji (w większości) z bezkształtnej :

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

Odpowiedź @Dmytrio to świetne wyjaśnienie, dlaczego odbicie nie działa zgodnie z oczekiwaniami.

Z twojego pytania rozumiem, że to, co próbujesz zrobić, to w rzeczywistości dopasowanie do wzorca wszystkich zmiennych, które masz w klasie przypadku. Rozważ zrobienie tego w następujący sposób:

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")
} 

Uruchom kod w Scastie .