【问题标题】:Trying to extract the TypeTag of a Sequence of classes that extend a trait with different generic type parameters尝试提取使用不同泛型类型参数扩展特征的类序列的 TypeTag
【发布时间】:2022-10-16 12:00:26
【问题描述】:

以下代码示例显示了我的问题的核心:

// This is the base trait that the classes are extending
trait Operation[T] {
  def operate(x: T): T
}

// Here are 2 case classes that exist for the sole purpose of being
// the generic type for the classes I'm making
case class CaseClass1(field1_1: String, field1_2: String, field1_3: String)
case class CaseClass2(field2_1: String, field2_2: String, field2_3: String)

// These are the 2 classes that extend my basic trait, implementing the operate
// method with some kind of logic
class FillField1 extends Operation[CaseClass1] {
  def operate(input: CaseClass1): CaseClass1 = input.copy(field1_3 = "haha")
}
class FillField2 extends Operation[CaseClass2] {
  def operate(input: CaseClass2): CaseClass2 = input.copy(field2_2 = "hoho")
}

import scala.reflect.runtime.universe._
// This is a function that prints out the typetag information currently available
def someFunc[T: TypeTag](x: Operation[T]): Unit = {
  println(s"TypeTag is: ${typeOf[T]}")
}

// Here I'm creating a sequence of Operations, but they can have any generic
// type parameter
val someSeq: Seq[Operation[_]] = Seq(new FillField1, new FillField2)

someSeq.map(someFunc(_))
/*
  Output:
  TypeTag is: _$1
  TypeTag is: _$1
*/

someFunc(new FillField1)
/*
  Output:
  TypeTag is: CaseClass1
*/

如您所见,当我调用someFunc(new fillField1) 时,我可以在运行时正确找到我的类型标签。但是当我使用someSeq,这是一个可以包含多种类型的类的序列时,我无法在运行时获得我需要的类型标签。这是因为您在运行时丢失了该信息吗?

如何在运行时获得正确的类型标签?那么,当我使用Seq[Operation[_]] 时,如何获得TypeTag is: CustomClass1TypeTag is: CustomClass2 的输出?

我正在研究一个 Apache Spark 项目,其中我们有一个与此类似的结构,当我使用该序列时,我遇到了一个问题,即 TypeTag 指向一个未知类_$10(或编译器制作的任何名称对于我的类型标签),而不是实际的类型标签 CustomClass1CustomClass2...

【问题讨论】:

  • 我猜这是因为someSeq 不保留每个元素的类型。而TypeTag 是在编译期间计算的,因此在编译时不可能提供 seq 的每个元素的类型。
  • 如果您真的需要它,您可以将TypeTag 存储为CaseClassX 类的属性。
  • 但是你想达到什么目的?
  • @Kurt 请用磁铁查看更新

标签: scala scala-reflect


【解决方案1】:

TypeTag 主要做的不是运行时反射,而是将一些信息(一种类型)从编译时保存到运行时。

Seq 是一个同构集合(即它的所有元素都具有相同的类型)。在Seq(new FillField1, new FillField2) 中,两个元素都具有Operation[_] 类型。因此,当应用 someFunc 时,T 被推断为 _ aka _$1(即存在类型的未知参数 Operation[_])。

因此,一种选择是使用异构集合 (HList)。然后元素可以有不同的类型,这些类型可以从编译时捕获到运行时,类型可以在运行时处理

import shapeless.{HNil, Poly1}

object someFuncPoly extends Poly1 {
  implicit def cse[T: TypeTag, O](implicit ev: O <:< Operation[T]): Case.Aux[O, Unit] =
    at(x => someFunc(x))
}

def someFunc[T: TypeTag](x: Operation[T]): Unit = println(s"Type is: ${typeOf[T]}")

(new FillField1 :: new FillField2 :: HNil).map(someFuncPoly)
//Type is: CaseClass1
//Type is: CaseClass2

另一种选择是使用运行时反射(即TypeTag 不做的事情)

import scala.reflect.runtime.universe._
import scala.reflect.runtime
val runtimeMirror = runtime.currentMirror

def someFunc(x: Operation[_]): Unit = {
  val xSymbol = runtimeMirror.classSymbol(x.getClass)
  val operationSymbol = xSymbol.baseClasses(1)// or just typeOf[Operation[_]].typeSymbol if you know Operation statically
  val extendee = xSymbol.typeSignature/*toType*/.baseType(operationSymbol)
  println(s"Type is: ${extendee.typeArgs.head}")
}

someSeq.map(someFunc(_))
//Type is: CaseClass1
//Type is: CaseClass2

另一种实现是

def someFunc(x: Operation[_]): Unit = {
  val xSymbol = runtimeMirror.classSymbol(x.getClass)
  val operationSymbol = xSymbol.baseClasses(1).asClass
  val operationParamType = operationSymbol.typeParams(0).asType.toType
  println(s"Type is: ${operationParamType.asSeenFrom(xSymbol.toType, operationSymbol)}")
}

另一种选择是磁铁图案(123456

trait Magnet[T] {
  def someFunc: Unit
}

import scala.language.implicitConversions

implicit def operationToMagnet[T: TypeTag](x: Operation[T]): Magnet[T] = new Magnet[T] {
  override def someFunc: Unit = println(s"TypeTag is: ${typeOf[T]}")
}

def someFunc[T: TypeTag](x: Operation[T]): Unit = (x: Magnet[T]).someFunc

Seq[Magnet[_]](new FillField1, new FillField2).map(_.someFunc)
// TypeTag is: CaseClass1
// TypeTag is: CaseClass2

【讨论】:

    猜你喜欢
    • 2016-07-04
    • 1970-01-01
    • 1970-01-01
    • 2023-02-23
    • 2016-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多