从反映的Java类实例中获取ClassTag



是否有可能从通过反射获得的Java Class实例中获得ClassTag信息?

情况是这样的。我有一个Scala case class,看起来像这样:

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

它是这样使用的(尽管有更多的类相互关联):

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

我想做的是用编程的方式建立一个这些关系的树,它看起来像这样:

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)

无法编译。我得到这个错误:

[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

基本上,我想做的是能够访问ClassTag信息时,我所有的是一个Java Class。这可能吗?

是的,这绝对是可能的,而且非常容易:

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

在您的示例中,您希望像这样调用getRelationModelClass方法:

getRelationModelClass(clazz)(ClassTag(clazz))

这是可能的,因为[T: ClassTag]语法隐式地创建第二个参数列表,如(implicit ct: ClassTag[T])。通常它是由编译器填充的,但没有什么可以阻止你显式地使用它。

您也不需要同时将此控件的class和class标记传递给方法。您甚至没有在其主体中使用显式类对象。只需传递class标签,就足够了。

我最终使用TypeTags和Scala反射API实现了我的目标。以下是必要的修改。

首先,将Relation类改为使用TypeTag。

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

然后更改其余代码以使用Scala反射API:

private def generateFieldMap(tpe: Type): Map[String, Type] =
    tpe.members.filter(_.asTerm.isVal).foldLeft(Map.empty[String, Type])((map, field) => {
        map + (member.name.toString.trim -> member.typeSignature)
    })
private def getRelationModelType(tpe: Type): Type = 
    tpe match { case TypeRef(_, _, args) => args.head }
def treeOf[M: TypeTag](relations: List[String]): Map[String, Any] = {
    val normalizedRelations = ModelHelper.normalize(relations)
    val initialFieldMap = Map("" -> generateFieldMap(typeTag[T].tpe))
    val relationFieldMap = relations.foldLeft(initialFieldMap)((map, relation) => {
        val parts = relation.split('.')
        val parentRelation = parts.dropRight(1).mkString(".")
        val relationType = map(parentRelation)(parts.last)
        val relationModelType = getRelationModelType(relationType)
        val fieldMap = generateFieldMap(relationModelType)
        map + (relation -> fieldMap)
    })
    relationFieldMap
}

相关内容

  • 没有找到相关文章

最新更新