2010-04-18 6 views
11

Итак, у меня есть класс Scala, который выглядит следующим образом:Любой способ доступа к типу объявления Scala Option во время выполнения с использованием отражения?

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). Я предпочел бы решение, которое не требовало бы аннотаций по цели отражения, если это было возможно.

+1

Я не вижу необходимости отражения. Любое значение, статическим типом которого является 'Option [Int]', либо 'None', либо' Some [Int] '. –

+0

Привет Рэндалл. Мне нужно использовать отражение, потому что класс, который манипулирует полями, например TestClass.value, не имеет доступа во время компиляции к классам, которые он манипулирует. Я добавил пример к моему вопросу, показывающий, как манипулируют целевыми объектами, и выделяя точку, в которой мне нужен ответ на этот вопрос. –

ответ

4

Это довольно streightforward с помощью 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) 
} 
+0

Потрясающие. Благодарю. Для записи я работаю над Fields, а не с методами. Код, который я использую, выглядит следующим образом: 'if (field.getType == classOf [Option [_]]) val optionFieldType = field.getGenericType.asInstanceOf [ParameterizedType] .getActualTypeArguments() (0)' –

+2

Scala автоматически обматывает поля методами: foo для getter и foo_ $ eq для setter. Поэтому лучше использовать эти методы-оболочки, а затем поля - в случае, если они будут переопределены в подклассе. В противном случае вы нарушите ожидаемое поведение подкласса. – Alexey

+3

Я нахожу, что «Свойство« GenericParameterType »из« Option [Int] »является« объектом »для моего класса case Scala (scala 2.10). Есть ли способ восстановить тип «Int»? – Rich

2
class TestClass { 
    var value: Option[Int] = None 
// ... 

    def doSomething { 
    value match { 
     case Some(i) => // i is an Int here 
     case None => 
     // No other possibilities 
    } 
    } 
} 
+0

Нет, код, который управляет TestClass, делает это через отражение - он не знает TestClass во время компиляции и должен обнаруживать тип, включая его параметр типа, во время выполнения. –

1

Проблема в том, что 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)) 
+0

Reflection знает (во время выполнения), что параметр/результат функции точно Опция Alexey

+0

Но она не знает этого о типах значений и полей, и об этом и шла речь. –

+0

Alexey прав: Как вы говорите, параметры типа стираются из подписи при компиляции, но, похоже, они все еще существуют в байтовом коде в той форме, которую Reflection API может извлечь и использовать. Посмотрите на ответ. –

3

На уровне байт-кода, Java не получил Обобщения. Дженерики реализуются с полиморфизмом, поэтому, когда ваш исходный код (в данном случае Scala) скомпилирован, исчезают общие типы (это называется type erasure). Это не дает возможности собирать информацию о типах во время выполнения через отражение.

Возможное - хотя немного грязное - обходное решение - получить тип среды выполнения, который, как известно, имеет тот же тип, что и параметр Generic. Для Выбора случаях мы можем использовать 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 
    } 
} 
+0

Спасибо за ваш ответ - много полезной информации.К сожалению, я ожидаю, что значение этих vars обычно будет None в то время, когда я хочу узнать тип, что означает, что значение не поможет мне получить тип. –

+0

Совсем нет. В любом случае, если значение времени выполнения vars равно None в большинстве случаев, вы можете принуждать его к scala.Nothing или scala.Null, поскольку они являются подклассами каждого потомка scala.AnyVal и scala.AnyRef соответственно – Miguel