Любой способ доступа к типу объявления Scala Option во время выполнения с использованием отражения?
Итак, у меня есть класс Scala, который выглядит так:
class TestClass {
var value: Option[Int] = None
}
и я решаю проблему, когда у меня есть значение String, и я хочу принудить ее к этому опцион [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 - Int.
Каковы мои параметры для выяснения того, что тип "значение" является опцией [Int] во время выполнения (т.е. с использованием отражения)?
Я видел подобные проблемы, решенные путем аннотации поля, например. @OptionType (int.class). Я бы предпочел решение, которое не требовало аннотаций по цели отражения, если это возможно.
Ответы
Ответ 1
Это довольно непросто с использованием API отражения Java 1.5:
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)
}
Ответ 2
На уровне байтового кода Java не имеет Generics. Generics реализованы с полиморфизмом, поэтому, когда ваш исходный код (в данном случае Scala) скомпилирован, исчезают общие типы (это называется type erasure). Это не позволяет собирать информацию типа типа во время выполнения через отражение.
Возможное - хотя немного грязное - обходное решение - получить тип среды выполнения, который, как известно, имеет тот же тип, что и параметр Generic. Для экземпляров Option мы можем использовать get
member
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
}
}
Ответ 3
class TestClass {
var value: Option[Int] = None
// ...
def doSomething {
value match {
case Some(i) => // i is an Int here
case None =>
// No other possibilities
}
}
}
Ответ 4
Проблема в том, что 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))