Любой способ получить доступ к типу объявлению опции Scala во время выполнения с использованием отражения?
-
27-09-2019 - |
Вопрос
Итак, у меня есть класс Scala, который выглядит так:
class TestClass {
var value: Option[Int] = None
}
И я решаю проблему, где у меня есть строковое значение, и я хочу, чтобы привлечь его к этому варианту [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)
}
Для этого мне нужно знать, что поле является опцией, и что параметр типа опции int.
Каковы мои варианты выяснения, что тип «значения» является опцией [int] во время выполнения (т. Е. Использование отражения)?
Я видел подобные проблемы, решаемые путем аннотации поля, например @optiontype (int.class). Я бы предпочел решение, которое не требовало аннотаций в целевой цели отражения.
Решение
Это довольно страшный, используя Java 1.5 Reflection API:
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) скомпилирован, общие типы исчезают (это называется Тип стирания ). Это не позволяет собирать общую информацию о времени выполнения в результате отражения.
Возможна - это маленький грязный - обходной путь состоит в том, чтобы получить тип выполнения свойства, которую вы знаете, он имеет тот же тип, что и универсальный параметр. Для экземпляров опционов мы можем использовать 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))