为 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] = T
或type As[T] = Seq[T]
。对于任何X
,我希望能够以X
或Seq[X]
的形式获得具体类型。第一种情况已经对我有用,因为在这种情况下,我只需返回我已经拥有的X
的Type
。第二种情况更难 - 我有一个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]的主要内容,如果未能解决你的问题,请参考以下文章