简体   繁体   English

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

[英]How to obtain generic type at runtime in Scala

I have a following class 我有以下课程

package myapp.model

case class Person(
 name: String,
 age: Option[Int]
)

I would like to implement following function: 我想实现以下功能:

def getFieldClass(className: String, fieldName:String): java.lang.Class[_] = {
    // case normal field return its class
    // case Option field return generic type of Option
}

So that for following input: 这样以下输入:

  • className="myapp.model.Person" 类名= “myapp.model.Person”
  • fieldName="age" 字段名=“年龄”

the function will return class object: scala.Int 该函数将返回类对象:scala.Int


Solution with Java Reflection API doesn't work well, it returns java.lang.Object for Option[Int]: 使用Java Reflection API的解决方案不能很好地工作,它返回Option [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)
}

I'm writing part of deserializing feature and I don't have the object to check it's type, I have only a class name. 我正在编写反序列化功能的一部分,我没有检查它的类型的对象,我只有一个类名。

You can accomplish this with Scala's reflection library. 您可以使用Scala的反射库来完成此任务。

It's not especially pretty though: 虽然它不是特别漂亮:

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

Then: 然后:

ReflectionHelper.getFieldClass("myapp.model.Person", "age")  // int
ReflectionHelper.getFieldClass("myapp.model.Person", "name") // class java.lang.String

I would recommend changing the return type of getFieldClass to be optional in case the field value doesn't make sense! 我建议将getFieldClass的返回类型更改为可选,以防字段值没有意义!

Maybe this can help you. 也许这可以帮到你。 TypeTags and manifests. TypeTags和清单。

For example, we can write a method which takes some arbitrary object, and using a TypeTag, prints information about that object's type arguments: 例如,我们可以编写一个方法来获取一些任意对象,并使用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") } 

Here, we write a generic method paramInfo parameterized on T, and we supply an implicit parameter (implicit tag: TypeTag[T]). 在这里,我们编写一个在T上参数化的泛型方法paramInfo,并提供一个隐式参数(隐式标记:TypeTag [T])。 We can then directly access the type (of type Type) that tag represents using method tpe of TypeTag. 然后,我们可以使用TypeTag的方法tpe直接访问标记表示的类型(类型为Type)。

We can then use our method paramInfo as follows: 然后我们可以使用我们的方法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) 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM