Так, у меня есть класс Scala, который похож на это:
class TestClass {
var value: Option[Int] = None
}
и я занимаюсь проблемой, где у меня есть Строковое значение, и я хочу принудить его в ту Опцию [Интервал] во времени выполнения с помощью отражения. Так, в другой части кода (который ничего не знает о 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)
}
Чтобы сделать это, я должен знать, что полем является Опция и что параметр типа Опции является Интервалом.
Каковы мои опции для выяснения, что тип 'значения' является Опцией [Интервал] во времени выполнения (т.е. использование отражения)?
Я видел подобные проблемы, решенные путем аннотирования поля, например, @OptionType (Int.class). Я предпочел бы решение, которое не потребовало аннотаций на отражательную цель, если это возможно.
С помощью 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)
}
Проблема в том, что JVM реализует универсальные шаблоны посредством стирания типов. Таким образом, невозможно обнаружить посредством отражения, что типом значения
является Option [Int]
, потому что во время выполнения на самом деле это не так: это просто Option
!
В 2.8 вы должны иметь возможность использовать манифесты
следующим образом:
var value: Option[Int] = None
val valueManifest = implicitly[scala.reflect.Manifest[Option[Int]]]
valueManifest.typeArguments // returns Some(List(Int))
class TestClass {
var value: Option[Int] = None
// ...
def doSomething {
value match {
case Some(i) => // i is an Int here
case None =>
// No other possibilities
}
}
}
На уровне байтового кода Java не имеет универсальных шаблонов. Обобщения реализованы с помощью полиморфизма, поэтому после компиляции исходного кода (в данном случае Scala) универсальные типы исчезают (это называется стиранием типа ). Это делает невозможным сбор информации об общем типе среды выполнения посредством отражения.
Возможный - хотя и немного грязный - обходной путь - получить тип среды выполнения для свойства, которое, как вы знаете, имеет тот же тип, что и параметр 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
}
}