【问题标题】:Get ClassTag from reflected Java Class instance从反射的 Java 类实例中获取 ClassTag
【发布时间】:2014-06-22 03:52:29
【问题描述】:

是否可以从通过反射获得的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

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

【问题讨论】:

    标签: java scala reflection


    【解决方案1】:

    是的,这绝对有可能而且非常简单:

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

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

    getRelationModelClass(clazz)(ClassTag(clazz))
    

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

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

    【讨论】:

    • 哇,这比我尝试的东西容易多了
    • 实际上这并没有什么帮助,因为我需要 Relation[M] 中的类型。例如,如果我执行ClassTag(clazz).runtimeClass,我会得到Relation 类,但不是我想要的M 类。
    • @cdmckay,对不起,我错过了你打电话给classTag[M],括号里是M。在这种情况下,我想,你运气不好。除非您使用具体的类型参数调用该方法,否则您将无法从Relation[M] 中检索M,仅给定Relation 的Java 类。那是可怕的类型擦除。
    • @cdmckay,其实你可以尝试使用TypeTags 和Scala 反射工具来代替类和类标签。但是你需要将它用于所有事情。然后你应该能够恢复泛型类型参数。
    • 谢谢,我能够通过 TypeTags 和 Scala 反射 API 完成它
    【解决方案2】:

    我最终使用 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
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-14
      • 2011-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多