Scala:抽象 case 类 的 toString 函数

Scala: Abstracting the toString function of case classes

假设我有一个案例class定义如下:

case class User(name: String, age: Int)

我想像这样覆盖它的 toString 方法:

 case class User(name: String, age: Int) {
    override def toString: String = 
      s"name = $name
        age = $age"

所以如果我 运行 print(user.toString),我得到:

name = nameOfUser
age = ageOfUser

现在我有另一个名为 Computer 的 class,它被定义为

case class Computer(make: String, RAM: int, clockSpeed: Double)

我想为它的所有值打印相同的内容。我想要这样的东西:

  make = makeOfComputer
  RAM = RAMOfComputer
  clockSpeed = speedOfComputer

与其将上述 toString 函数复制、粘贴和改编到 Computer class,我宁愿将其抽象化,以便它可以在任何情况下使用 class.

我有一些想法可以使用

 CaseClassType.unapply(caseClassInstance).get.productIterator.toList

获取案例class和

的值
 classOf[CaseClass].getDeclaredFields.map(_.getName)

获取字段名称。这意味着我可以在完全不知道 case class 的实际结构的情况下找到 case class 中所有值的列表以及所有字段名称的列表。

一旦我有了这两个集合,我就可以递归地遍历它们来创建字符串。我在想像下面这样的东西可以工作但不幸的是scala不允许 case classes 从其他 case classes.

继承
case class StringifiableCaseClass(){
  override def toString: String =
     //get the values and the fieldnames and create a string

我所要做的就是让案例 classes 扩展 StringifiableCaseClass,他们将能够创建正确的字符串。

之所以要用case class作为基本类型是因为我可以用

 classOf[this.type]...etc

  this.type.unapply(this)...etc

StringifiableCaseClass 中。

关于如何实现我想要的东西的任何想法我不能将案例 classes 扩展到其他案例 classes?

如果我正确理解了你的用例,你可以使用 Cats 默认 Show type-class 或者,如果你需要更多自定义行为,你可以直接使用泛型推导 Shapeless 实现你所追求的。

有关 Cat Show 更简单的场景,请阅读 here

如您所见,它的语法可让您对范围内具有 Show[T] 的任何 T 执行 myObject.show

如果您想要更可定制的行为,您可以尝试直接使用 Shapeless。在 this example Show type-class and you can see it running here.

中可以找到您需要的示例

鉴于这些案例 classes:

  sealed trait Super
  case class Foo(i: Int, s: String) extends Super
  case class Bar(i: Int) extends Super
  case class BarRec(i: Int, rec: Super) extends Super

  sealed trait MutualA
  case class MutualA1(x: Int) extends MutualA
  case class MutualA2(b: MutualB) extends MutualA

  sealed trait MutualB
  case class MutualB1(x: Int) extends MutualB
  case class MutualB2(b: MutualA) extends MutualB

它将打印:

Bar(i = 0)   
BarRec(i = 1, rec = Foo(i = 0, s = foo))   
MutualA2(b = MutualB2(b = MutualA1(x = 0)))

基本实现不一定是案例class。让它成为一个特征,然后你的案例 classes 都可以扩展它。

 trait Stringification { self: Product => 
    override def toString() = 
      getClass.getDeclaredFields
      .zip(productIterator.toSeq)
      .map { case (a, b) => s"${a.getName}=$b" }
      .mkString("\n")
 }

然后

  case class Foo(foo: String, bar: Int) extends Stringification
  case class Bar(foo: Foo, bar: String) extends Stringification

...等等