尝试提取类序列的TypeTag,这些类序列使用不同的泛型类型参数扩展特性



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

// 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时,它是一个可以包含多种类型类的Sequence,我无法在运行时获得所需的typetag。这是因为您在运行时丢失了这些信息吗?

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

我正在做一个Apache Spark项目,我们有一个类似的结构,当我使用该序列时,我遇到了一个问题,即TypeTag指向一个未知类_$10(或编译器为我的TypeTag创建的任何名称(,而不是实际的TypeTag,它将是CustomClass1CustomClass2。。。

TypeTag主要做的不是运行时反射,而是从编译时到运行时持久化一些信息(一种类型(。

Seq是一个同构集合(即它的所有元素都具有相同的类型(。在CCD_ 11中,两个元素都具有类型CCD_。因此,当应用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)}")
}

还有一个选项是磁铁图案(1 2 3 4 5 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

最新更新