有什么方法可以使用反射在运行时访问 Scala 选项声明的类型吗?
-
27-09-2019 - |
题
所以,我有一个 Scala 类,如下所示:
class TestClass {
var value: Option[Int] = None
}
我正在解决一个问题,我有一个 String 值,并且我想在运行时使用反射将其强制到该 Option[Int] 中。因此,在另一段代码(对 TestClass 一无所知)中,我有一些如下代码:
def setField[A <: Object](target: A, fieldName: String, value: String) {
val field = target.getClass.getDeclaredField(fieldName)
val coercedValue = ???; // How do I figure out that this needs to be Option[Int] ?
field.set(target, coercedValue)
}
为此,我需要知道该字段是一个 Option 并且该 Option 的类型参数是 Int。
我有哪些选项可以在运行时确定“值”的类型是 Option[Int] (即使用反射)?
我已经看到通过注释字段解决了类似的问题,例如@OptionType(Int.class)。如果可能的话,我更喜欢不需要在反射目标上进行注释的解决方案。
解决方案
这是相当使用Java 1.5反射API streightforward:
def isIntOption(clasz: Class[_], propertyName: String) = {
var result =
for {
method <- cls.getMethods
if method.getName==propertyName+"_$eq"
param <- method.getGenericParameterTypes.toList.asInstanceOf[List[ParameterizedType]]
} yield
param.getActualTypeArguments.toList == List(classOf[Integer])
&& param.getRawType == classOf[Option[_]]
if (result.length != 1)
throw new Exception();
else
result(0)
}
其他提示
在字节码级别,Java 还没有泛型。泛型是通过多态性实现的,因此一旦源代码(在本例中为 Scala)被编译,泛型类型就会消失(这称为 类型擦除 )。这使得无法通过反射收集通用运行时类型信息。
一个可能的(虽然有点脏)解决方法是获取属性的运行时类型,您知道它与通用参数具有相同的类型。对于 Option 实例,我们可以使用 get
成员
object Test {
def main(args : Array[String]) : Unit = {
var option: Option[_]= None
println(getType(option).getName)
option = Some(1)
println(getType(option).getName)
}
def getType[_](option:Option[_]):Class[_]= {
if (option.isEmpty) classOf[Nothing] else (option.get.asInstanceOf[AnyRef]).getClass
}
}
class TestClass {
var value: Option[Int] = None
// ...
def doSomething {
value match {
case Some(i) => // i is an Int here
case None =>
// No other possibilities
}
}
}
问题在于 JVM 通过类型擦除来实现泛型。所以不可能通过反思发现 value
是 Option[Int]
因为在运行时它实际上不是:只是 Option
!
在 2.8 中你应该能够使用 Manifests
像这样:
var value: Option[Int] = None
val valueManifest = implicitly[scala.reflect.Manifest[Option[Int]]]
valueManifest.typeArguments // returns Some(List(Int))
不隶属于 StackOverflow