scala 尝试提取一系列类的TypeTag,这些类使用不同的泛型类型参数扩展特征

z5btuh9x  于 2022-11-09  发布在  Scala
关注(0)|答案(1)|浏览(148)

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

// 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作为输出呢?
我正在处理一个ApacheSpark项目,其中我们有一个类似的结构,当我使用该序列时,我收到一个问题,即TypeTag指向一个未知的类_$10(或编译器为我的类型标签命名的任何名称),而不是实际的TypeTag,它应该是CustomClass1CustomClass2……

h43kikqp

h43kikqp1#

TypeTag主要做的不是运行时反射,而是将一些信息(一种类型)从编译时持久化到运行时。
Seq是同构集合(即其所有元素都具有相同的类型)。在Seq(new FillField1, new FillField2)中,两个元素的类型都是Operation[_]。因此,当应用someFunc时,T被推断为_,也就是_$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)}")
}

另一个选项是磁铁图案(12345 6 7)

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

或者,您可以将someFunc移动到Operation内部,将TypeTag从方法移动到类,并使Operation成为抽象类

abstract class Operation[T: TypeTag] {
  def operate(x: T): T

  def someFunc: Unit = {
    println(s"TypeTag is: ${typeOf[T]}")
  }
}
(new FillField1).someFunc //TypeTag is: CaseClass1
(new FillField2).someFunc //TypeTag is: CaseClass2

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

相关问题