Scala 3中如何通过反射获取案例类的字段值

问题描述 投票:0回答:1

我正在迁移过去使用 Scala 2 反射的代码。

我正在尝试使用 scala-reflection 库。

我需要获取案例类给定名称的字段值。

示例:

$ scala-cli -j system -S 3.4.2 --dep co.blocke::scala-reflection:2.0.8
scala> import co.blocke.scala_reflection.RType

scala> case class A(a: Int, b: String)
// defined case class A

scala> import co.blocke.scala_reflection.rtypes.ScalaClassRType

scala> val t = RType.of[A].asInstanceOf[ScalaClassRType[A]]
val t: co.blocke.scala_reflection.rtypes.ScalaClassRType[A] = ScalaClassRType(rs$line$2$A,rs$line$2$.A,List(),List(),List(),List(ScalaFieldInfo(0,a,IntRType(),Map(),None,None,false), ScalaFieldInfo(1,b,StringRType(),Map(),None,None,false)),Map(),List(java.lang.Object, scala.Product, java.io.Serializable),false,false,true,false,Map(),List(),List(),false)

scala> t.fields.find(_.name == "a")
val res2: Option[co.blocke.scala_reflection.rtypes.FieldInfo] = Some(ScalaFieldInfo(0,a,IntRType(),Map(),None,None,false))

scala> val c = A(3,"hola")
val c: A = A(3,hola)

在这个例子中,如何通过实例

a
的反射来获取字段
c
的值。

如果更容易,我可以考虑使用其他库,例如izumi-reflect

scala reflection scala-3
1个回答
0
投票

假设您想通过将事物一个接一个地放入 JDBC 中来序列化:

trait SerializationTarget
object SerializationTarget {
  val example: SerializationTarget = new SerializationTarget {}
}

这是一个存根,我将用作您传递来调用某些 API 的东西。

trait SerializeForJDBC[A] {

  def serialize(value: A, target: SerializationTarget): Unit
}

这将是一个类型类,它将采用

A
的值,并以某种方式将其所有数据放入
SerializationTarget
中 - 但看起来会像这样。

我们将从一些(存根)实例开始,展示如何处理原语:

object SerializeForJDBC {

  given SerializeForJDBC[Int] with
    def serialize(value: Int, target: SerializationTarget): Unit = ()
  given SerializeForJDBC[Double] with
    def serialize(value: Double, target: SerializationTarget): Unit = ()
  given SerializeForJDBC[String] with
    def serialize(value: String, target: SerializationTarget): Unit = ()
}

extension [A](value: A)
  final def serializeForJDBC(target: SerializationTarget)(using
      SerializeForJDBC[A]
  ): Unit =
    summon[SerializeForJDBC[A]].serialize(value, target)

现在,当我们打电话时

10.serializeForJDBC(SerializationTarget.example)
20.0.serializeForJDBC(SerializationTarget.example)
"test".serializeForJDBC(SerializationTarget.example)

代码会选择正确的

given
并使用它,基本上就像策略模式一样,但该策略是基于类型的 DI-ed。

最后,我们将使用

Mirror
为每个
case class
生成这样的策略:

object SerializeForJDBC {
  // other givens ...

  import scala.compiletime.*
  import scala.deriving.*

  inline given derived[A <: Product](using
      p: Mirror.ProductOf[A]
  ): SerializeForJDBC[A] = {
    new SerializeForJDBC[A] {
      // - p.MirroredElemTypes is a path-dependent type, which is a tuple
      //   containing types for the fields of a case class in order in which
      //   they were defined
      // - Tuple.Map turns it into (SerializeForJDBC[Field1], SerializeForJDBC[Field2], ...)
      // - summonAll takes the type of a tuple and returns a tuple of givens
      // - that's why we had to define givens before and put them into scope
      //   e.g. by putting them into the companion object of a type class
      private val instances =
        summonAll[Tuple.Map[p.MirroredElemTypes, SerializeForJDBC]].toList
          .asInstanceOf[List[SerializeForJDBC[Any]]]

      def serialize(value: A, target: SerializationTarget): Unit =
        // the easiest way of passing values into mirrors is to cast everything
        // as Any and TypeClass[Any] and make sure that indices are correct
        // e.g. using .zip
        value.productIterator.zip(instances).foreach { case (field, instance) =>
          instance.asInstanceOf[SerializeForJDBC[Any]].serialize(field, target)
        }
    }
  }
}

最后我们可以称之为:

case class Example(a: Int, b: Double, c: String)

Example(1, 2.0, "3").serializeForJDBC(SerializationTarget.example)

每次需要时它都会创建一个新的

SerializeForJDBC[Example]
实例。这应该不是什么大问题,但如果我们想缓存实例,我们可以使用
derives
关键字(因为我们将基于镜像的方法定义为
derived
):

case class Example(a: Int, b: Double, c: String) derived SerializeForJDBC

Example(1, 2.0, "3").serializeForJDBC(SerializationTarget.example)

在实际代码中,您必须用与 JDBC 交互的方式替换

SerializationTarget
,替换原始
given
的主体,并调整将每个字段的实例组合成整个
case class
的实例的方式.

© www.soinside.com 2019 - 2024. All rights reserved.