13

所以,我有一个看起来像这样的 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 回答 4

5

使用 Java 1.5 反射 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)
 }
于 2010-04-19T20:06:48.993 回答
3

在字节码级别,Java 没有泛型。泛型是通过多态性实现的,因此一旦您的源代码(在本例中为 Scala)被编译,泛型类型就会消失(这称为类型擦除)。这使得无法通过反射收集通用运行时类型信息。

一种可能的解决方法(尽管有点脏)是获取您知道它与通用参数具有相同类型的属性的运行时类型。对于 Option 实例,我们可以使用getmember

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
  }
}
于 2010-04-19T10:51:43.667 回答
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
    }
  }
}
于 2010-04-19T00:17:27.163 回答
1

问题在于 JVM 通过类型擦除来实现泛型。所以不可能通过反射发现类型valueOption[Int]因为在运行时它实际上不是:它只是Option

在 2.8 中你应该可以这样使用Manifests

var value: Option[Int] = None
val valueManifest = implicitly[scala.reflect.Manifest[Option[Int]]]
valueManifest.typeArguments // returns Some(List(Int))
于 2010-04-19T10:29:38.583 回答