Alguma maneira de acessar o tipo de declaração de opção Scala em tempo de execução usando a reflexão?

StackOverflow https://stackoverflow.com/questions/2664114

Pergunta

Então, eu tenho uma aula de Scala que se parece com a seguinte:

class TestClass {
  var value: Option[Int] = None
}

E estou abordando um problema em que tenho um valor de string e quero coagi -lo nessa opção [int] no tempo de execução usando a reflexão. Então, em outro pedaço de código (que não sabe nada sobre testclass), tenho algum código como este:

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)   
}

Para fazer isso, preciso saber que o campo é uma opção e que o parâmetro de tipo da opção é int.

Quais são minhas opções para descobrir que o tipo de 'valor' é a opção [int] no tempo de execução (ou seja, usando a reflexão)?

Vi problemas semelhantes resolvidos anotando o campo, por exemplo, @OptionType (int.class). Eu preferiria uma solução que não exigisse anotações no alvo de reflexão, se possível.

Foi útil?

Solução

É bastante Streightforward usando a API de reflexão 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)
 }

Outras dicas

No nível do código de bytes, o Java não tem genéricos. Os genéricos são implementados com o polimorfismo; portanto, uma vez que seu código -fonte (neste caso Scala) é compilado, os tipos genéricos desaparecem (isso é chamado tipo de apagamento ). Isso não torna possível reunir informações genéricas do tipo de tempo de execução via reflexão.

Uma solução possível-embora pouca solução alternativa seja para obter o tipo de tempo de execução de uma propriedade, você sabe que ele tem o mesmo tipo que o parâmetro genérico. Para instâncias de opção, podemos usar get membro

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
    }
  }
}

O problema é que a JVM implementa genéricos através do apagamento do tipo. Portanto, é impossível descobrir através da reflexão que o tipo de value é Option[Int] Porque no tempo de execução, na verdade não é: é apenas Option!

Em 2.8, você deve ser capaz de usar Manifests assim:

var value: Option[Int] = None
val valueManifest = implicitly[scala.reflect.Manifest[Option[Int]]]
valueManifest.typeArguments // returns Some(List(Int))
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top