Java 如何在Scala中获得运行时的泛型类型
我有一节以下的课Java 如何在Scala中获得运行时的泛型类型,java,scala,reflection,option,Java,Scala,Reflection,Option,我有一节以下的课 包myapp.model 案例类人员( 名称:String, 年龄:选项[Int] ) 我想实现以下功能: def getFieldClass(className:String,fieldName:String):java.lang.Class[\u]={ //case normal字段返回其类 //案例选项字段返回选项的通用类型 } 因此,对于以下输入: className=“myapp.model.Person” fieldName=“年龄” 函数将返回类对象:sc
包myapp.model
案例类人员(
名称:String,
年龄:选项[Int]
)
我想实现以下功能:
def getFieldClass(className:String,fieldName:String):java.lang.Class[\u]={
//case normal字段返回其类
//案例选项字段返回选项的通用类型
}
因此,对于以下输入:
- className=“myapp.model.Person”
- fieldName=“年龄”
带有Java反射API的解决方案无法正常工作,它返回选项[Int]的Java.lang.Object:
def getFieldClass(className: String, fieldName:String): java.lang.Class[_] = {
val cls = java.lang.Class.forName(className)
val pt = cls.getDeclaredField(fieldName).getGenericType.asInstanceOf[java.lang.reflect.ParameterizedType]
val tpe = pt.getActualTypeArguments()(0);
java.lang.Class.forName(tpe.getTypeName)
}
我正在编写反序列化功能的一部分,我没有对象来检查它的类型,我只有一个类名。也许这可以帮助您。
例如,我们可以编写一个方法,该方法接受某个任意对象,并使用TypeTag打印有关该对象类型参数的信息:
import scala.reflect.runtime.universe._
def paramInfo[T](x: T)(implicit tag: TypeTag[T]): Unit = {
val targs = tag.tpe match { case TypeRef(_, _, args) => args }
println(s"type of $x has type arguments $targs")
}
在这里,我们编写了一个在T上参数化的泛型方法paramInfo,并且
提供隐式参数(隐式标记:TypeTag[T])。那么我们可以
使用直接访问标记表示的类型(类型)
TypeTag的方法tpe
然后,我们可以使用我们的方法paramInfo,如下所示:
scala> paramInfo(42)
type of 42 has type arguments List()
scala> paramInfo(List(1, 2))
type of List(1, 2) has type arguments List(Int)
您可以使用Scala的反射库来实现这一点 但它并不特别漂亮:
import scala.reflect.runtime.{ universe => u }
import scala.reflect.runtime.universe._
object ReflectionHelper {
val classLoader = Thread.currentThread().getContextClassLoader
val mirror = u.runtimeMirror(classLoader)
def getFieldType(className: String, fieldName: String): Option[Type] = {
val classSymbol = mirror.staticClass(className)
for {
fieldSymbol <- classSymbol.selfType.members.collectFirst({
case s: Symbol if s.isPublic && s.name.decodedName.toString() == fieldName => s
})
} yield {
fieldSymbol.info.resultType
}
}
def maybeUnwrapFieldType[A](fieldType: Type)(implicit tag: TypeTag[A]): Option[Type] = {
if (fieldType.typeConstructor == tag.tpe.typeConstructor) {
fieldType.typeArgs.headOption
} else {
Option(fieldType)
}
}
def getFieldClass(className: String, fieldName: String): java.lang.Class[_] = {
// case normal field return its class
// case Option field return generic type of Option
val result = for {
fieldType <- getFieldType(className, fieldName)
unwrappedFieldType <- maybeUnwrapFieldType[Option[_]](fieldType)
} yield {
mirror.runtimeClass(unwrappedFieldType)
}
// Consider changing return type to: Option[Class[_]]
result.getOrElse(null)
}
}
如果字段值没有意义,我建议将
getFieldClass
的返回类型更改为可选 就我所了解的scala文档而言,TypeTag在编译时工作。我只有字符串形式的运行时类名。看看这个。好吧,它对我也不起作用。我已经编辑了我的问题并添加了我没有实例可以处理的信息——我只有类的名称。如果我使用默认构造函数通过反射创建实例,那么可选的字段将用空值初始化。我还没有测试代码,但看起来确实很有希望。我会在几个小时内给你更新-提前谢谢@Brian Kent,有没有任何原因表明,当它与对象类(带字段)一起使用时,而不是与case类一起使用时,getFieldClass
返回object
?@skyler我不明白为什么它不应该工作。我对类Foo(val a:Int){val b:Option[Int]=?}
做了一个快速测试,两者都按预期工作(在Scala 2.12.8中)。@BrianKent我只注意到了一个问题,如果你做对象MyObject{val a:Option[Int]=None}
,它在类上工作得很好,真的很奇怪@skylerl我很惊讶它对对象
(因为使用了mirror.staticClass
而不是mirror.staticModule
)。您使用的是什么版本的Scala?
ReflectionHelper.getFieldClass("myapp.model.Person", "age") // int
ReflectionHelper.getFieldClass("myapp.model.Person", "name") // class java.lang.String