Java 如何在Scala中获得运行时的泛型类型

Java 如何在Scala中获得运行时的泛型类型,java,scala,reflection,option,Java,Scala,Reflection,Option,我有一节以下的课 包myapp.model 案例类人员( 名称:String, 年龄:选项[Int] ) 我想实现以下功能: def getFieldClass(className:String,fieldName:String):java.lang.Class[\u]={ //case normal字段返回其类 //案例选项字段返回选项的通用类型 } 因此,对于以下输入: className=“myapp.model.Person” fieldName=“年龄” 函数将返回类对象:sc

我有一节以下的课

包myapp.model
案例类人员(
名称:String,
年龄:选项[Int]
)
我想实现以下功能:

def getFieldClass(className:String,fieldName:String):java.lang.Class[\u]={
//case normal字段返回其类
//案例选项字段返回选项的通用类型
}
因此,对于以下输入:

  • className=“myapp.model.Person”
  • fieldName=“年龄”
函数将返回类对象:scala.Int


带有Java反射API的解决方案无法正常工作,它返回选项[Int]的Java.lang.Object:

def getFieldClass(className: String, fieldName:String): java.lang.Class[_] = {
    val cls = java.lang.Class.forName(className)
    val pt = cls.getDeclaredField(fieldName).getGenericType.asInstanceOf[java.lang.reflect.ParameterizedType]
    val tpe = pt.getActualTypeArguments()(0);
    java.lang.Class.forName(tpe.getTypeName)
}
我正在编写反序列化功能的一部分,我没有对象来检查它的类型,我只有一个类名。

也许这可以帮助您。

例如,我们可以编写一个方法,该方法接受某个任意对象,并使用TypeTag打印有关该对象类型参数的信息:

import scala.reflect.runtime.universe._
def paramInfo[T](x: T)(implicit tag: TypeTag[T]): Unit = {
  val targs = tag.tpe match { case TypeRef(_, _, args) => args }
  println(s"type of $x has type arguments $targs")
}
在这里,我们编写了一个在T上参数化的泛型方法paramInfo,并且 提供隐式参数(隐式标记:TypeTag[T])。那么我们可以 使用直接访问标记表示的类型(类型) TypeTag的方法tpe

然后,我们可以使用我们的方法paramInfo,如下所示:

scala> paramInfo(42)
type of 42 has type arguments List()
scala> paramInfo(List(1, 2))
type of List(1, 2) has type arguments List(Int)

您可以使用Scala的反射库来实现这一点

但它并不特别漂亮:

import scala.reflect.runtime.{ universe => u }
import scala.reflect.runtime.universe._

object ReflectionHelper {

  val classLoader = Thread.currentThread().getContextClassLoader

  val mirror = u.runtimeMirror(classLoader)

  def getFieldType(className: String, fieldName: String): Option[Type] = {

    val classSymbol = mirror.staticClass(className)

    for {
      fieldSymbol <- classSymbol.selfType.members.collectFirst({
        case s: Symbol if s.isPublic && s.name.decodedName.toString() == fieldName => s
      })
    } yield {

      fieldSymbol.info.resultType
    }
  }

  def maybeUnwrapFieldType[A](fieldType: Type)(implicit tag: TypeTag[A]): Option[Type] = {
    if (fieldType.typeConstructor == tag.tpe.typeConstructor) {
      fieldType.typeArgs.headOption
    } else {
      Option(fieldType)
    }
  }

  def getFieldClass(className: String, fieldName: String): java.lang.Class[_] = {

    // case normal field return its class
    // case Option field return generic type of Option

    val result = for {
      fieldType <- getFieldType(className, fieldName)
      unwrappedFieldType <- maybeUnwrapFieldType[Option[_]](fieldType)
    } yield {
      mirror.runtimeClass(unwrappedFieldType)
    }

    // Consider changing return type to: Option[Class[_]]
    result.getOrElse(null)
  }
}

如果字段值没有意义,我建议将
getFieldClass
的返回类型更改为可选

就我所了解的scala文档而言,TypeTag在编译时工作。我只有字符串形式的运行时类名。看看这个。好吧,它对我也不起作用。我已经编辑了我的问题并添加了我没有实例可以处理的信息——我只有类的名称。如果我使用默认构造函数通过反射创建实例,那么可选的字段将用空值初始化。我还没有测试代码,但看起来确实很有希望。我会在几个小时内给你更新-提前谢谢@Brian Kent,有没有任何原因表明,当它与对象类(带字段)一起使用时,而不是与case类一起使用时,
getFieldClass
返回
object
?@skyler我不明白为什么它不应该工作。我对
类Foo(val a:Int){val b:Option[Int]=?}
做了一个快速测试,两者都按预期工作(在Scala 2.12.8中)。@BrianKent我只注意到了一个问题,如果你做
对象MyObject{val a:Option[Int]=None}
,它在类上工作得很好,真的很奇怪@skylerl我很惊讶它对
对象
(因为使用了
mirror.staticClass
而不是
mirror.staticModule
)。您使用的是什么版本的Scala?
ReflectionHelper.getFieldClass("myapp.model.Person", "age")  // int
ReflectionHelper.getFieldClass("myapp.model.Person", "name") // class java.lang.String