Любой способ получить доступ к типу объявления опции Scala во время выполнения, используя отражение? - PullRequest
11 голосов
/ 19 апреля 2010

Итак, у меня есть класс 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)   
}

Для этого мне нужно знать, что поле является опцией, а параметр типа этой опции - Int.

Какие у меня есть варианты для определения того, что типом 'значения' является Option [Int] во время выполнения (то есть с использованием отражения)?

Я видел похожие проблемы, решаемые аннотированием поля, например, @OptionType (int.class). Я бы предпочел решение, которое не требует аннотаций к цели отражения, если это возможно.

Ответы [ 4 ]

4 голосов
/ 20 апреля 2010

Это довольно просто при использовании 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)
 }
3 голосов
/ 19 апреля 2010

На уровне байтового кода у 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
  }
}
2 голосов
/ 19 апреля 2010
class TestClass {
  var value: Option[Int] = None
// ...

  def doSomething {
    value match {
      case Some(i) => // i is an Int here
      case None =>
      // No other possibilities
    }
  }
}
1 голос
/ 19 апреля 2010

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