简体   繁体   English

获取指向另一个案例 Class 的案例 Class 定义

[英]Getting Case Class definition which points to another Case Class

I am looking at getting case class definitions.我正在寻找案例 class 的定义。

From SO I gleaned this practice as per Get field names list from case class , the answer using reflection by Dia Kharrat.从 SO 中,我根据案例 class 中的 Get field names list收集了这种做法,Dia Kharrat 使用反射的答案。

Some experimenting in which I have a case class referring to another case class, nested.一些实验,其中我有一个案例 class 引用另一个案例 class,嵌套。 Can we get the metadata expanded easily in some way?我们能否以某种方式轻松扩展元数据?

import scala.collection.mutable.ArrayBuffer

case class MyChgClass(b: Option[String], c: Option[String], d: Option[String])
case class MyFullClass(k: Int, b: String, c: String, d: String)
case class MyEndClass(id: Int, after: MyFullClass)

def classAccessors[T: TypeTag]: List[MethodSymbol] = typeOf[T].members.collect {
 case m: MethodSymbol if m.isCaseAccessor => m
}.toList

val z1 = classAccessors[MyChgClass]
val z2 = classAccessors[MyFullClass]
val z3 = classAccessors[MyEndClass]

returns:回报:

z1: List[reflect.runtime.universe.MethodSymbol] = List(value d, value c, value b)
z2: List[reflect.runtime.universe.MethodSymbol] = List(value d, value c, value b, value k)
z3: List[reflect.runtime.universe.MethodSymbol] = List(value after, value id)

So:所以:

  1. Looking to expand the case class MyEndClass.寻找扩展案例 class MyEndClass。
  2. The option aspect appears not not been supplied.选项方面似乎未提供。 Possible?可能的?
  1. The option aspect appears not not been supplied.选项方面似乎未提供。 Possible?可能的?

Are you looking for .name and .typeSignature ?您在寻找.name.typeSignature吗?

val z1 = classAccessors[MyChgClass]
val z2 = classAccessors[MyFullClass]
val z3 = classAccessors[MyEndClass]

z1.map(_.name) // List(d, c, b)
z1.map(_.typeSignature) // List(Option[String], Option[String], Option[String])
z2.map(_.name) // List(d, c, b, k)
z2.map(_.typeSignature) // List(String, String, String, Int)
z3.map(_.name) // List(after, id)
z3.map(_.typeSignature) // List(MyFullClass, Int)

If your classes are known at compile time it would make sense to use compile-time reflection ie macros rather than runtime reflection如果您的类在编译时已知,那么使用编译时反射即而不是 运行时反射是有意义的

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

def classAccessors[T]: List[(String, String)] = macro classAccessorsImpl[T]

def classAccessorsImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
  import c.universe._
  val pairs = weakTypeOf[T].members.collect {
    case m: MethodSymbol if m.isCaseAccessor => m
  }.map(m => (m.name.toString, m.typeSignature.toString))
  q"List.apply[(String, String)](..$pairs)"
}
// in a different subproject
classAccessors[MyChgClass] // List((d,Option[String]), (c,Option[String]), (b,Option[String]))
classAccessors[MyFullClass] // List((d,String), (c,String), (b,String), (k,Int))
classAccessors[MyEndClass] // List((after,MyFullClass), (id,Int))

Even better would be to use one of libraries encapsulating those macros into some type classes.更好的方法是使用将这些宏封装到某些类型类中的库之一。 In Shapeless the type class giving access to names and types of case-class fields is LabelledGenericShapeless中,类型 class 提供对案例类字段的名称和类型的访问权限是LabelledGeneric

// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.labelled.FieldType
import shapeless.ops.hlist.{FillWith, Mapper, ToList}
import shapeless.{HList, LabelledGeneric, Poly0, Poly1, Typeable, Witness}

object fieldNamesAndTypesPoly extends Poly1 {
  implicit def cse[K <: Symbol, V](implicit
    witness: Witness.Aux[K],
    typeable: Typeable[V]
  ): Case.Aux[FieldType[K, V], (String, String)] =
    at(_ => (witness.value.name, typeable.describe))
}

object nullPoly extends Poly0 {
  implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}

def classAccessors[T] = new PartiallyApplied[T]

class PartiallyApplied[T] {
  def apply[L <: HList, L1 <: HList]()(implicit
    labelledGeneric: LabelledGeneric.Aux[T, L],
    fillWith: FillWith[nullPoly.type, L],
    mapper: Mapper.Aux[fieldNamesAndTypesPoly.type, L, L1],
    toList: ToList[L1, (String, String)]
  ): List[(String, String)] = toList(mapper(fillWith()))
}
classAccessors[MyChgClass]() // List((b,Option[String]), (c,Option[String]), (d,Option[String]))
classAccessors[MyFullClass]() // List((k,Int), (b,String), (c,String), (d,String))
classAccessors[MyEndClass]() // List((id,Int), (after,MyFullClass))

  1. Looking to expand the case class MyEndClass .希望扩展案例 class MyEndClass

You can try deep versions of the type classes LabelledGeneric , Mapper etc.您可以尝试LabelledGenericMapper等类型类的深层版本。

import shapeless.labelled.{FieldType, field}
import shapeless.{::, DepFn0, DepFn1, HList, HNil, LabelledGeneric, Poly0, Poly1, Typeable, Witness, poly}

trait DeepLabelledGeneric[T <: Product] {
  type Repr <: HList
  def to(t: T): Repr
  def from(r: Repr): T
}

object DeepLabelledGeneric {
  type Aux[T <: Product, Repr0 <: HList] = DeepLabelledGeneric[T] {type Repr = Repr0}
  def instance[T <: Product, Repr0 <: HList](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new DeepLabelledGeneric[T] {
    override type Repr = Repr0
    override def to(t: T): Repr = f(t)
    override def from(r: Repr): T = g(r)
  }

  implicit def deepGeneric[A <: Product, L <: HList, L1 <: HList](implicit
    labelledGeneric: LabelledGeneric.Aux[A, L],
    hListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[L, L1]
  ): Aux[A, L1] = instance(a => hListDeepLabelledGeneric.to(labelledGeneric.to(a)), l1 => labelledGeneric.from(hListDeepLabelledGeneric.from(l1)))
}

trait HListDeepLabelledGeneric[T <: HList] {
  type Repr <: HList
  def to(t: T): Repr
  def from(r: Repr): T
}

trait LowPriorityHListDeepLabelledGeneric {
  type Aux[T <: HList, Repr0 <: HList] = HListDeepLabelledGeneric[T] {type Repr = Repr0}

  def instance[T <: HList, Repr0 <: HList](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] = new HListDeepLabelledGeneric[T] {
    override type Repr = Repr0
    override def to(t: T): Repr = f(t)
    override def from(r: Repr): T = g(r)
  }

  implicit def headNotCaseClass[H, T <: HList, T_hListDeepLGen <: HList](implicit
    tailHListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[T, T_hListDeepLGen]
  ): Aux[H :: T, H :: T_hListDeepLGen] = instance({
    case h :: t => h :: tailHListDeepLabelledGeneric.to(t)
  }, {
    case h :: t => h :: tailHListDeepLabelledGeneric.from(t)
  })
}

object HListDeepLabelledGeneric extends LowPriorityHListDeepLabelledGeneric {
  implicit val hNil: Aux[HNil, HNil] = instance(identity, identity)

  implicit def headCaseClass[K <: Symbol, H <: Product, T <: HList, H_deepLGen <: HList, T_hListDeepLGen <: HList](implicit
    headDeepLabelledGeneric: DeepLabelledGeneric.Aux[H, H_deepLGen],
    tailHListDeepLabelledGeneric: HListDeepLabelledGeneric.Aux[T, T_hListDeepLGen]
  ): Aux[FieldType[K, H] :: T, FieldType[K, H_deepLGen] :: T_hListDeepLGen] = instance({
    case h :: t => field[K](headDeepLabelledGeneric.to(h)) :: tailHListDeepLabelledGeneric.to(t)
  }, {
    case h :: t => field[K](headDeepLabelledGeneric.from(h)) :: tailHListDeepLabelledGeneric.from(t)
  })
}


trait DeepMapper[P <: Poly1, In <: HList] extends DepFn1[In] {
  type Out <: HList
}

trait LowPriorityDeepMapper {
  def apply[P <: Poly1, L <: HList](implicit deepMapper: DeepMapper[P, L]): Aux[P, L, deepMapper.Out] = deepMapper
  type Aux[P <: Poly1, In <: HList, Out0 <: HList] = DeepMapper[P, In] {type Out = Out0}
  def instance[P <: Poly1, In <: HList, Out0 <: HList](f: In => Out0): Aux[P, In, Out0] = new DeepMapper[P, In] {
    override type Out = Out0
    override def apply(t: In): Out = f(t)
  }

  implicit def headNotHList[P <: Poly1, H, T <: HList](implicit
    headCase: poly.Case1[P, H],
    tailDeepMapper: DeepMapper[P, T]
  ): Aux[P, H :: T, headCase.Result :: tailDeepMapper.Out] =
    instance(l => headCase(l.head) :: tailDeepMapper(l.tail))
}

object DeepMapper extends LowPriorityDeepMapper {
  implicit def hNil[P <: Poly1]: Aux[P, HNil, HNil] = instance(_ => HNil)

  implicit def headHList[P <: Poly1, K <: Symbol, H <: HList, H_deepMap <: HList, T <: HList](implicit
    headDeepMapper: DeepMapper.Aux[P, H, H_deepMap],
    headCase: poly.Case1[P, FieldType[K, H_deepMap]],
    tailDeepMapper: DeepMapper[P, T]
  ): Aux[P, FieldType[K, H] :: T, headCase.Result :: tailDeepMapper.Out] =
    instance(l => headCase(field[K](headDeepMapper(l.head))) :: tailDeepMapper(l.tail))
}

trait DeepFillWith[P <: Poly0, L <: HList] extends DepFn0 {
  type Out = L
}

trait LowPriorityDeepFillWith {
  def apply[P <: Poly0, L <: HList](implicit deepFillWith: DeepFillWith[P, L]): DeepFillWith[P, L] = deepFillWith
  def instance[P <: Poly0, L <: HList](f: => L): DeepFillWith[P, L] = new DeepFillWith[P, L] {
    override def apply(): L = f
  }

  implicit def headNotHList[P <: Poly0, H, T <: HList](implicit
    headCase: poly.Case0.Aux[P, H],
    tailDeepFillWith: DeepFillWith[P, T]
  ): DeepFillWith[P, H :: T] =
    instance(headCase() :: tailDeepFillWith())
}

object DeepFillWith extends LowPriorityDeepFillWith {
  implicit def hNil[P <: Poly0]: DeepFillWith[P, HNil] = instance(HNil)

  implicit def headHList[P <: Poly1, K <: Symbol, H <: HList, T <: HList](implicit
    headDeepMapper: DeepMapper[P, H],
    tailDeepMapper: DeepMapper[P, T]
  ): Aux[P, FieldType[K, H] :: T, FieldType[K, headDeepMapper.Out] :: tailDeepMapper.Out] =
    instance(l => field[K](headDeepMapper(l.head)) :: tailDeepMapper(l.tail))
}

trait LowPriorityFieldNamesAndTypesPoly extends Poly1 {
  implicit def notHListCase[K <: Symbol, V](implicit
    witness: Witness.Aux[K],
    typeable: Typeable[V]
  ): Case.Aux[FieldType[K, V], (String, String)] =
    at(_ => (witness.value.name, typeable.describe))
}

object fieldNamesAndTypesPoly extends LowPriorityFieldNamesAndTypesPoly {
  implicit def hListCase[K <: Symbol, V <: HList](implicit
    witness: Witness.Aux[K],
  ): Case.Aux[FieldType[K, V], (String, V)] =
    at(v => (witness.value.name, v))
}

object nullPoly extends Poly0 {
  implicit def cse[A]: Case0[A] = at(null.asInstanceOf[A])
}

def classAccessors[T <: Product] = new PartiallyApplied[T]

class PartiallyApplied[T <: Product] {
  def apply[L <: HList]()(implicit
    deepLabelledGeneric: DeepLabelledGeneric.Aux[T, L],
    deepFillWith: DeepFillWith[nullPoly.type, L],
    deepMapper: DeepMapper[fieldNamesAndTypesPoly.type, L],
  ): deepMapper.Out = deepMapper(deepFillWith())
}
classAccessors[MyChgClass]() // (b,Option[String]) :: (c,Option[String]) :: (d,Option[String]) :: HNil
classAccessors[MyFullClass]() // (k,Int) :: (b,String) :: (c,String) :: (d,String) :: HNil
classAccessors[MyEndClass]() // (id,Int) :: (after,(k,Int) :: (b,String) :: (c,String) :: (d,String) :: HNil) :: HNil

Deriving nested shapeless lenses using only a type 仅使用类型派生嵌套的无形透镜

Weird behavior trying to convert case classes to heterogeneous lists recursively with Shapeless 尝试使用 Shapeless 递归地将案例类转换为异构列表的奇怪行为

https://github.com/milessabin/shapeless/blob/main/examples/src/main/scala/shapeless/examples/deephlister.scala https://github.com/milessabin/shapeless/blob/main/examples/src/main/scala/shapeless/examples/deephlister.scala

Converting nested case classes to nested Maps using Shapeless 使用 Shapeless 将嵌套案例类转换为嵌套地图

Automatically convert a case class to an extensible record in shapeless? 自动将案例 class 转换为无形的可扩展记录?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM