我有一个旧的Scala / Java混合项目。它具有一个自定义的自制ORM映射工具,该工具生成一堆Java实体类,然后在Scala中与它们一起使用,将它们从/映射到自定义属性映射和yaml文件。
今天,我偶然发现了一个问题,ORM工具将List<String>
内的所有字符串都转换为Array[Byte]
的映射(这导致将数据进一步转换为java.io.ByteArrayInputStream而不是简单的Scala列表。字符串)。
在尝试解决此问题并将List[String]
添加到ORM字段值匹配器时,我逐字删除了。不幸的是,我无法控制自动工具生成的Java类,因此无法使用TypeTags等。反思似乎是我唯一的希望。
这是有问题的用例的简化示例。
Java实体:
public class SubEntity {
}
import java.util.ArrayList;
import java.util.List;
public class Entity {
protected List<SubEntity> field1;
protected List<String> field2;
public List<SubEntity> getField1() {
if (field1 == null) {
field1 = new ArrayList<SubEntity>();
}
return this.field1;
}
public List<String> getField2() {
if (field2 == null) {
field2 = new ArrayList<String>();
}
return this.field2;
}
}
一段有问题的Scala代码:
val ent = new Entity
ent.getClass.getMethods
.filter (m => m.getName.startsWith("get") && m.getName != "getClass")
.filter (m => m.getParameterTypes.size == 0)
.foreach (m => m.invoke(ent) match {
case null => null
// Not able to differentiate between List[String] and List[SomeEntity] and Array[Byte[
// at runtime because of type erasure :(
// How to solve this?
case s: java.util.List[java.lang.String] => {
println(s"Found list of strings in ${m.getName}, the values are ${s.toList}")
}
case s: java.util.List[SubEntity] => {
println(s"Found list of SubEntity in ${m.getName}")
}
case _ => println(s"Unknown ${m.getName}")
})
根据我注释掉的块,它会打印
Found list of strings
或
Found list of SubEntity
同时针对两个字段,并且无法正确确定getField1
返回List[SubEntity]
和getField2
返回List[java.lang.String]
。
我该如何解决问题并使匹配根据需要进行?
答案 0 :(得分:0)
在以下文章的帮助下,我自己设法做到了这一点:http://tutorials.jenkov.com/java-reflection/generics.html#returntypes
我创建了一个辅助方法,用于检查返回类型的泛型并验证其是否包含请求的参数:
import java.lang.reflect.ParameterizedType
def doesMethodReturnGenericSubtype(objectClass: Class[_], methodName: String,
typeClassToCheck: Class[_]) = {
val method = objectClass.getMethod(methodName)
val returnType = method.getGenericReturnType
// assume no type known
var typeFound = false
if (returnType.isInstanceOf[ParameterizedType]) {
val pType = returnType.asInstanceOf[ParameterizedType]
val typeArguments = pType.getActualTypeArguments
for (typeArgument <- typeArguments) {
val typeArgClass = typeArgument.asInstanceOf[Class[_]]
if (typeArgClass.getName == typeClassToCheck.getName)
typeFound = true
}
}
typeFound
}
像这样使用它:
val ent = new Entity
ent.getClass.getMethods
.filter (m => m.getName.startsWith("get") && m.getName != "getClass")
.filter (m => m.getParameterTypes.size == 0)
.foreach (m => m.invoke(ent) match {
case null => null
case s: java.util.Collection[_] if doesMethodReturnGenericSubtype(ent.getClass,
m.getName, classOf[java.lang.String]) => {
println(s"Found list of strings in ${m.getName}, the values are ${s.toList}")
}
case s: java.util.Collection[_] if doesMethodReturnGenericSubtype(ent.getClass,
m.getName, classOf[SubEntity]) => {
println(s"Found list of SubEntity in ${m.getName}")
}
case x => println(s"Unknown ${m.getName} in ${x.getClass.getName}")
})
我仍然不确定getActualTypeArguments
是否能够提取出“擦除”的泛型类型,但是以某种方式可以正常工作。