是否可以从通过反射获得的Java ClassTag
实例中获取Class
信息?
这是情况。我有一个看起来像这样的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
时,我想要做的就是能够访问Class
信息。这可能吗?
答案 0 :(得分:5)
是的,绝对可能也很容易:
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的类AND类标记传递给该方法。您甚至没有在其正文中使用显式类对象。只需传递类标记,就足够了。
答案 1 :(得分:1)
我最终使用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
}