De toute façon d'accéder au type d'une déclaration d'option Scala lors de l'exécution en utilisant la réflexion?

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

Question

Alors, j'ai une classe Scala qui ressemble à ceci:

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

et je lutte contre un problème où j'ai une valeur de chaîne et je veux le contraindre dans cette option [Int] lors de l'exécution en utilisant la réflexion. Ainsi, dans un autre morceau de code (qui ne sait rien à propos TestClass) J'ai un code comme ceci:

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

Pour ce faire, je dois savoir que le champ est une option et que le paramètre de type de l'option est Int.

Quelles sont mes options pour déterminer que le type de « valeur » est l'option [Int] lors de l'exécution (à savoir en utilisant la réflexion)?

J'ai vu des problèmes similaires résolus en annotant le champ, par exemple @OptionType (Int.class). Je préfère une solution qui ne nécessite pas d'annotations sur la cible de réflexion si possible.

Était-ce utile?

La solution

Il est tout à fait streightforward en utilisant Java 1.5 API de réflexion:

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

Autres conseils

Au niveau du code octet, Java n'a pas obtenu Generics. Génériques sont mises en œuvre avec le polymorphisme, donc une fois que votre code source (dans ce cas, Scala) est compilé, les types génériques disparaissent (ce qu'on appelle type effacement ). Cela ne fait pas possible de recueillir des informations de type d'exécution générique par réflexion.

Une possible --though peu dirty-- solution de contournement est d'obtenir le type d'exécution d'une propriété que vous savez qu'il a le même type que le paramètre générique. Pour les instances de l'option, nous pouvons utiliser membre 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
    }
  }
}

Le problème est que JVM met en œuvre des génériques par l'effacement de type. Il est donc impossible de découvrir par la réflexion que le type de value est Option[Int] car au moment de l'exécution, il est en fait pas: il est juste Option

2.8, vous devriez être en mesure d'utiliser Manifests comme ceci:

var value: Option[Int] = None
val valueManifest = implicitly[scala.reflect.Manifest[Option[Int]]]
valueManifest.typeArguments // returns Some(List(Int))
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top