Scala:反射和案例类

Ant*_*and 8 reflection scala case-class shapeless scala-reflect

以下代码成功,但是有更好的方法做同样的事情吗?也许是案例类特有的东西?在下面的代码中,对于我的简单案例类中String类型的每个字段,代码遍历我的案例类的实例列表,并查找该字段的最长字符串的长度.

case class CrmContractorRow(
                             id: Long,
                             bankCharges: String,
                             overTime: String,
                             name$id: Long,
                             mgmtFee: String,
                             contractDetails$id: Long,
                             email: String,
                             copyOfVisa: String)

object Go {
  def main(args: Array[String]) {
    val a = CrmContractorRow(1,"1","1",4444,"1",1,"1","1")
    val b = CrmContractorRow(22,"22","22",22,"55555",22,"nine long","22")
    val c = CrmContractorRow(333,"333","333",333,"333",333,"333","333")
    val rows = List(a,b,c)

    c.getClass.getDeclaredFields.filter(p => p.getType == classOf[String]).foreach{f =>
      f.setAccessible(true)
      println(f.getName + ": " + rows.map(row => f.get(row).asInstanceOf[String]).maxBy(_.length))
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

结果:

bankCharges: 3
overTime: 3
mgmtFee: 5
email: 9
copyOfVisa: 3
Run Code Online (Sandbox Code Playgroud)

Tra*_*own 11

如果你想用Shapeless做这种事情,我强烈建议你定义一个处理复杂部分的自定义类型,并允许你将这些东西与其余的逻辑分开.

在这种情况下,它听起来像你特别想要做的那个棘手的部分是从String一个案例类的所有成员获得从字段名到字符串长度的映射.这是一个类型类,它执行此操作:

import shapeless._, shapeless.labelled.FieldType

trait StringFieldLengths[A] { def apply(a: A): Map[String, Int] }

object StringFieldLengths extends LowPriorityStringFieldLengths {
  implicit val hnilInstance: StringFieldLengths[HNil] =
    new StringFieldLengths[HNil] {
      def apply(a: HNil): Map[String, Int] = Map.empty
    }

  implicit def caseClassInstance[A, R <: HList](implicit
    gen: LabelledGeneric.Aux[A, R],
    sfl: StringFieldLengths[R]
  ): StringFieldLengths[A] = new StringFieldLengths[A] {
    def apply(a: A): Map[String, Int] = sfl(gen.to(a))
  }

  implicit def hconsStringInstance[K <: Symbol, T <: HList](implicit
    sfl: StringFieldLengths[T],
    key: Witness.Aux[K]
  ): StringFieldLengths[FieldType[K, String] :: T] =
    new StringFieldLengths[FieldType[K, String] :: T] {
      def apply(a: FieldType[K, String] :: T): Map[String, Int] =
        sfl(a.tail).updated(key.value.name, a.head.length)
    }
}

sealed class LowPriorityStringFieldLengths {
  implicit def hconsInstance[K, V, T <: HList](implicit
    sfl: StringFieldLengths[T]
  ): StringFieldLengths[FieldType[K, V] :: T] =
    new StringFieldLengths[FieldType[K, V] :: T] {
      def apply(a: FieldType[K, V] :: T): Map[String, Int] = sfl(a.tail)
    }
}
Run Code Online (Sandbox Code Playgroud)

这看起来很复杂,但是一旦你开始使用Shapeless,你就会学会在睡梦中写下这种东西.

现在,您可以以相对简单的方式编写操作的逻辑:

def maxStringLengths[A: StringFieldLengths](as: List[A]): Map[String, Int] =
  as.map(implicitly[StringFieldLengths[A]].apply).foldLeft(
    Map.empty[String, Int]
  ) {
    case (x, y) => x.foldLeft(y) {
      case (acc, (k, v)) =>
        acc.updated(k, acc.get(k).fold(v)(accV => math.max(accV, v)))
    }
  }
Run Code Online (Sandbox Code Playgroud)

然后(rows按照问题中的定义给出):

scala> maxStringLengths(rows).foreach(println)
(bankCharges,3)
(overTime,3)
(mgmtFee,5)
(email,9)
(copyOfVisa,3)
Run Code Online (Sandbox Code Playgroud)

这适用于绝对任何案例类.

如果这是一次性的事情,你也可以使用运行时反射,或者你可以Poly1在Giovanni Caporaletti的答案中使用这种方法 - 它不那么通用,它以我不喜欢的方式混合了解决方案的不同部分,但它应该工作得很好.如果这是你做了很多事情的话,我会建议我在这里给出的方法.