2
votes

Question

How do you determine the type of each field of a class?

Given the following case class:

case class GenericCaseClass(
    a: Boolean,
    b: Byte,
    c: Short,
    d: Int,
    e: Long,
    f: Float,
    g: Double,
    h: Char,
    i: String,
    j: Option[Boolean],
    k: Option[Byte],
    l: Option[Short],
    m: Option[Int],
    n: Option[Long],
    o: Option[Float],
    p: Option[Double],
    q: Option[Char],
    r: Option[String]
)

Initial Attempt

import java.lang.reflect.{Field, ParameterizedType}

def printType(field: Field): Unit = {
    val scalaClass = field.getType

    if (scalaClass == classOf[Boolean]) {
        println("Boolean")
    } else if (scalaClass == classOf[Byte]) {
        println("Byte")
    }
    ...
    } else if (scalaClass == classOf[Option[Boolean]]) {
        println("Boolean")
    } else if (scalaClass == classOf[Option[Byte]]) {
        println("Byte")
    }
    ...
}

classOf[GenericCaseClass].getDeclaredFields.foreach(
    declaredField => {
        printType(declaredField)
    }
)

Initial Result

  • Boolean
  • Byte
  • Short
  • Int
  • Long
  • Float
  • Double
  • Char
  • String
  • Option[Boolean]
  • Option[Boolean]
  • Option[Boolean]
  • Option[Boolean]
  • Option[Boolean]
  • Option[Boolean]
  • Option[Boolean]
  • Option[Boolean]
  • Option[Boolean]

Current Attempt

I added the following to the beginning of the if-statement chain in an attempt to get the inner type of the Options:

if (scalaClass == classOf[Option[_]]) {
    val innerType = field
        .getGenericType
        .asInstanceOf[ParameterizedType]
        .getActualTypeArguments
        .head
        .getTypeName

    println("Option[_] -> " + innerType)
}

But it appears to only work for Strings:

  • Boolean
  • Byte
  • Short
  • Int
  • Long
  • Float
  • Double
  • Char
  • String
  • Option[_] -> java.lang.Object
  • Option[_] -> java.lang.Object
  • Option[_] -> java.lang.Object
  • Option[_] -> java.lang.Object
  • Option[_] -> java.lang.Object
  • Option[_] -> java.lang.Object
  • Option[_] -> java.lang.Object
  • Option[_] -> java.lang.Object
  • Option[_] -> java.lang.String

Note: I'm using Scala version 2.11.11.

2
You are using JVM runtime reflection to reflect on static Scala types. That cannot possibly work. a) the JVM and Scala are two completely different things, the JVM doesn't know anything whatsoever about Scala. (In this particular case: the JVM doesn't have generics, but you are trying to reflect on generic types.) b) Runtime reflection cannot necessarily tell you about static types. (In this particular case: Scala performs generic type erasure, i.e. generic types are erased at compile time and don't exist at runtime, there is no difference between Option[Foo] and Option[Bar]).Jörg W Mittag
@JörgWMittag stackoverflow.com/a/26501056/5065400 You can get Scala types, I based my solution off of this answer.Randomness Slayer

2 Answers

1
votes

Using Java reflection quickly turned into a headache for me, so here is a pretty simple Scala solution:

import scala.reflect.runtime.universe._

//--Your case class definition--//

val values = typeOf[GenericCaseClass].members.filterNot(_.isMethod)

def prettyPrintField(symbol: Symbol): Unit = {
  val name = symbol.name
  val typeSignature: Type = symbol.typeSignature
  println(s"$name: $typeSignature")
}

values.foreach(prettyPrintField)

Output:

r : Option[String]
q : Option[Char]
<- rest of fields ->
a : Boolean
1
votes

Use ClassTag to access runtime type information:

import scala.reflect.ClassTag
def getOptionType[T](opt: Option[T])(implicit t: ClassTag[T]) = t.runtimeClass