为 T 已知运行时反射地创建 Seq[T]

Posted

技术标签:

【中文标题】为 T 已知运行时反射地创建 Seq[T]【英文标题】:Create Seq[T] reflectively for a T known runtime 【发布时间】: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 存储在包装器伴侣中。 【参考方案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
    
  

【讨论】:

以上是关于为 T 已知运行时反射地创建 Seq[T]的主要内容,如果未能解决你的问题,请参考以下文章

C#关于反射创建泛型类

C#从反射类型实例化通用列表[重复]

一种查找发射光线的角度以便击中目标的方法。已知源和目标

Go语言(十五) 反射

<T> 在运行时 ProtoBuf-net 模型中是不是允许?

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