Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get ClassTag from reflected Java Class instance

Is it possible to get ClassTag information from a Java Class instance obtained via reflection?

Here's the situation. I have a Scala case class that looks like this:

case class Relation[M : ClassTag](id: UUID, 
                                  model: Option[M] = None)

And it is used like this (although with many more classes related to each other):

case class Organization(name: String)

case class Person(firstName: String, 
                  lastName: String,
                  organization: Relation[Organization])

What I'm trying to do is programmatically build up a tree of these relations using something that looks like this:

private def generateFieldMap(clazz: Class[_]): Map[String, Class[_]] = {
    clazz.getDeclaredFields.foldLeft(Map.empty[String, Class[_]])((map, field) => {
        map + (field.getName -> field.getType)
    })
}

private def getRelationModelClass[M : ClassTag](relationClass: Class[_ <: Relation[M]]): Class[_] = {
    classTag[M].runtimeClass
}

def treeOf[M: ClassTag](relations: List[String]): Map[String, Any] = {
    val normalizedRelations = ModelHelper.normalize(relations)
    val initialFieldMap = Map("" -> generateFieldMap(classTag[M].runtimeClass))
    val relationFieldMap = relations.foldLeft(initialFieldMap)((map, relation) => {
        val parts = relation.split('.')
        val parentRelation = parts.dropRight(1).mkString(".")
        val relationClass = map(parentRelation)(parts.last)
        val relationModelClass = relationClass match {
            case clazz: Class[_ <: Relation[_]] => getRelationModelClass(clazz)
            case _ => throw ProcessStreetException("cannot follow non-relation: " + relation)
        }
        val fieldMap = generateFieldMap(relationModelClass)
        map + (relation -> fieldMap)
    })
    relationFieldMap
}

val relations = List("organization")
val tree = treeOf[Person](relations)

This won't compile. I get this error:

[error] Foo.scala:148: not found: type _$12
[error]                 case clazz: Class[_ <: Relation[_]] => getRelationModelClass(clazz)
[error]                                   ^
[error] one error found
[error] (compile:compile) Compilation failed

Basically, what I'd like to do is be able to access the ClassTag information when all I have is a Java Class. Is this possible?

like image 599
cdmckay Avatar asked May 05 '14 18:05

cdmckay


1 Answers

Yes, it is absolutely possible and very easy:

val clazz = classOf[String]
val ct = ClassTag(clazz)  // just use ClassTag.apply() method

In your example you'd want to call getRelationModelClass method like this:

getRelationModelClass(clazz)(ClassTag(clazz))

This is possible because [T: ClassTag] syntax implicitly creates second parameters list like (implicit ct: ClassTag[T]). Usually it is filled by the compiler, but nothing prevents you from using it explicitly.

You also don't really need to pass the class AND class tag for this clazz at the same time to the method. You're not even using explicit class object in its body. Just pass the class tag, it will be enough.

like image 127
Vladimir Matveev Avatar answered Sep 19 '22 23:09

Vladimir Matveev