【问题标题】:Create Seq[T] reflectively for a T known runtime为 T 已知运行时反射地创建 Seq[T]
【发布时间】:2021-05-06 17:25:10
【问题描述】:

我有一个类型 T 已知反射运行时作为 scala.reflect.runtime.Type 的值。我想创建一个代表scala.collection.Seq[T] 的类型值。我该怎么做?

我想要这个的背景:

我希望能够提取包含在各种类型的伴随对象中的反射泛型类型。这些类型的形式为type As[T] = Ttype As[T] = Seq[T]。对于任何X,我希望能够以XSeq[X] 的形式获得具体类型。第一种情况已经对我有用,因为在这种情况下,我只需返回我已经拥有的XType。第二种情况更难 - 我有一个Type 用于Seq[T],这是通用的,Type 用于具体X,但我不知道如何修改我已经拥有或创建的Seq[T]一个全新的Seq[X]

import scala.reflect.runtime.universe._

object Wrap {
  type As[T] = T
}
case class Wrap[T](value: T)

object WrapSeq {
  type As[T] = Seq[T]
}
case class WrapSeq[T](value: Seq[T])

object Main extends App {

  def checkType(typeSignature: Type): Type = {
    // find mapping in the companion object
    val companion = typeSignature.typeSymbol.companion
    val member = companion.typeSignature.member(TypeName("As"))

    if (member != NoSymbol && member.isType) {
      val memberType = member.asType

      def extractSingleType(types: Seq[Type]): Option[Type] = types match {
        case Seq(head) => Some(head)
        case _ => None
      }
      (memberType.typeParams, typeSignature.typeArgs) match {
        case (Seq(typePar), Seq(typeArg)) if memberType.isAliasType =>
          if (member.typeSignature.resultType.typeSymbol.fullName == typePar.fullName) {
            // As[T] == T
            typeArg
          } else if (member.typeSignature.resultType.typeSymbol.fullName == "scala.collection.Seq" && extractSingleType(member.typeSignature.resultType.typeArgs).exists(_.typeSymbol.name == typePar.name)) {
            // As[T] == Seq[T]
            // we want to return Seq[typeArg] here
            // we need to construct the type reflectively somehow
            ???
          } else {
            throw new UnsupportedOperationException(s"Unsupported As value $typePar for $typeSignature")
          }
        case _ => // no type parameter, handle the plain type
          member.typeSignature
      }
    } else {
      typeSignature
    }
  }

  println(checkType(typeOf[Wrap[String]])) // output: String

  println(checkType(typeOf[WrapSeq[String]])) // desired output: Seq[String]

}

【问题讨论】:

  • 你实际上想达到什么目标?
  • 我正在为我序列化的类创建一个 JSON 模式。 As 类型别名用于描述序列化编码器结果(编码器为 Borer 或 Circe)。
  • 如果您知道您尝试序列化的类的类型,您不妨使用编译时反射。这样您就不会遇到类型擦除问题
  • 是的,这是另一种解决方案(它有其自身的复杂性)。在我尝试这种方式之前,我正在检查反射是否可以得到我需要的东西。上面的代码已经处理了类型擦除(我有我需要的具体类型)。它是“唯一”构建Seq[X],我想念它完全正常工作。
  • 如果您正在考虑 hacky 解决方案,您可以将运行时类型的 X 存储在包装器伴侣中。

标签: scala types reflection


【解决方案1】:

它表明答案实际上非常简单,只要您知道要寻找什么。使用 appliedType 正是我所需要的:

appliedType(typeOf[Seq[_]], typeArg)

使用appliedType 甚至可以使checkType 函数更短且更灵活:它现在可以处理任何泛型类型,而不仅仅是Seq

  def checkType(typeSignature: Type): Type = {
    // find mapping in the companion object
    val companion = typeSignature.typeSymbol.companion
    val member = companion.typeSignature.member(TypeName("As"))

    if (member != NoSymbol && member.isType) {
      if (member.asType.typeParams.size == typeSignature.typeArgs.size) {
        appliedType(member.typeSignature, typeSignature.typeArgs)
      } else {
        throw new UnsupportedOperationException(s"Type parameters not matching in JsonType value ${member.typeSignature} for $typeSignature")
      }
    } else {
      typeSignature
    }
  }

【讨论】:

    猜你喜欢
    • 2018-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多