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

4nkexdtk  于 2022-11-09  发布在  Scala
关注(0)|答案(2)|浏览(210)

我有一个以下班级

package myapp.model

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

我想实现以下功能:

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

因此,对于以下输入:

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

我正在编写反序列化功能的一部分,我没有对象来检查它的类型,我只有一个类名。

xxslljrj

xxslljrj1#

您可以使用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)
  }
}

然后:

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

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

2g32fytz

2g32fytz2#

也许这能帮到你。TypeTags and manifests.
例如,我们可以编写一个接受某个任意对象的方法,并使用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直接访问Tag表示的类型(类型为Type)。
然后,我们可以按如下方式使用我们的方法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)

相关问题