Reputation: 686
I have a following class
package myapp.model
case class Person(
name: String,
age: Option[Int]
)
I would like to implement following function:
def getFieldClass(className: String, fieldName:String): java.lang.Class[_] = {
// case normal field return its class
// case Option field return generic type of Option
}
So that for following input:
the function will return class object: scala.Int
Solution with Java Reflection API doesn't work well, it returns java.lang.Object for Option[Int]:
def getFieldClass(className: String, fieldName:String): java.lang.Class[_] = {
val cls = java.lang.Class.forName(className)
val pt = cls.getDeclaredField(fieldName).getGenericType.asInstanceOf[java.lang.reflect.ParameterizedType]
val tpe = pt.getActualTypeArguments()(0);
java.lang.Class.forName(tpe.getTypeName)
}
I'm writing part of deserializing feature and I don't have the object to check it's type, I have only a class name.
Upvotes: 1
Views: 2676
Reputation: 3854
You can accomplish this with Scala's reflection library.
It's not especially pretty though:
import scala.reflect.runtime.{ universe => u }
import scala.reflect.runtime.universe._
object ReflectionHelper {
val classLoader = Thread.currentThread().getContextClassLoader
val mirror = u.runtimeMirror(classLoader)
def getFieldType(className: String, fieldName: String): Option[Type] = {
val classSymbol = mirror.staticClass(className)
for {
fieldSymbol <- classSymbol.selfType.members.collectFirst({
case s: Symbol if s.isPublic && s.name.decodedName.toString() == fieldName => s
})
} yield {
fieldSymbol.info.resultType
}
}
def maybeUnwrapFieldType[A](fieldType: Type)(implicit tag: TypeTag[A]): Option[Type] = {
if (fieldType.typeConstructor == tag.tpe.typeConstructor) {
fieldType.typeArgs.headOption
} else {
Option(fieldType)
}
}
def getFieldClass(className: String, fieldName: String): java.lang.Class[_] = {
// case normal field return its class
// case Option field return generic type of Option
val result = for {
fieldType <- getFieldType(className, fieldName)
unwrappedFieldType <- maybeUnwrapFieldType[Option[_]](fieldType)
} yield {
mirror.runtimeClass(unwrappedFieldType)
}
// Consider changing return type to: Option[Class[_]]
result.getOrElse(null)
}
}
Then:
ReflectionHelper.getFieldClass("myapp.model.Person", "age") // int
ReflectionHelper.getFieldClass("myapp.model.Person", "name") // class java.lang.String
I would recommend changing the return type of getFieldClass
to be optional in case the field value doesn't make sense!
Upvotes: 3
Reputation: 113
Maybe this can help you. TypeTags and manifests.
For example, we can write a method which takes some arbitrary object, and using a TypeTag, prints information about that object’s type arguments:
import scala.reflect.runtime.universe._ def paramInfo[T](x: T)(implicit tag: TypeTag[T]): Unit = { val targs = tag.tpe match { case TypeRef(_, _, args) => args } println(s"type of $x has type arguments $targs") }
Here, we write a generic method paramInfo parameterized on T, and we supply an implicit parameter (implicit tag: TypeTag[T]). We can then directly access the type (of type Type) that tag represents using method tpe of TypeTag.
We can then use our method paramInfo as follows:
scala> paramInfo(42) type of 42 has type arguments List() scala> paramInfo(List(1, 2)) type of List(1, 2) has type arguments List(Int)
Upvotes: 1