How to find class parameter datatype at runtime in scala

断了今生、忘了曾经 提交于 2020-12-15 05:26:12

问题


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

Not able to digest why result is Absurd instead of I am int?

My aim is to know data-type of class parameter at runtime and match it to predefined types.


回答1:


Both current dataType and typeOf[Int] are printed as Int but if you do showRaw you'll see why they don't match

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

The thing is that just the type Int and the type of nullary method returning Int are different types.

Try to add .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

It's also worth mentioning that .decl(TermName("id")) returns getter symbol, it's .decl(TermName("id ")) (with a blank space) that returns field symbol. So alternatively you can do with a blank space in the symbol name and without .resultType

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

I'll add to @TomerShetah's answer that if the goal is "pattern matching" all fields of a case class then this can be done also at compile time (mostly) with 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




回答2:


@Dmytrio answer is a great explanation why the reflection didn't work as you expected.

I can understand from your question, that what you are trying to do, is actually pattern match all variables you have in a case class. Please consider doing it in the following way:

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 run at Scastie.



来源:https://stackoverflow.com/questions/65062245/how-to-find-class-parameter-datatype-at-runtime-in-scala

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!