Suppose I have a case class defined as follows:
case class User(name: String, age: Int)
I would like to override its toString
method like this:
case class User(name: String, age: Int) {
override def toString: String =
s"name = $name
age = $age"
So that if I run print(user.toString)
, I get:
name = nameOfUser
age = ageOfUser
Now I have another class called Computer
and it is defined as
case class Computer(make: String, RAM: int, clockSpeed: Double)
I would like to print the same for all its values. I would like something like:
make = makeOfComputer
RAM = RAMOfComputer
clockSpeed = speedOfComputer
Instead of copying, pasting and adapting the above toString
function to the Computer
class, I'd rather abstract this away so that it can be used by any case class.
Some ideas I have are that I can use
CaseClassType.unapply(caseClassInstance).get.productIterator.toList
to get the values of the case class and
classOf[CaseClass].getDeclaredFields.map(_.getName)
to get the names of the fields. What this means is that I can find a list of all the values in a case class as well as a list of all the field names without knowing the actual structure of the case class at all.
Once I have these two collections, I can recursively go through them to create the string. I was thinking something like the below could work but unfortunately scala doesn't allow case classes to inherit from other case classes.
case class StringifiableCaseClass(){
override def toString: String =
//get the values and the fieldnames and create a string
All I'd have to do is get the case classes to extend StringifiableCaseClass
and they would be able to create the right string.
The reason why I want to use a case class as the base type is that I can use
classOf[this.type]...etc
and
this.type.unapply(this)...etc
in StringifiableCaseClass
.
Any ideas on how I can achieve what I am after given I cannot extend case classes with other case classes?
答案 0 :(得分:5)
基本实现不必是case类。只需使其成为一个特征,然后您的case类就可以对其进行扩展。
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
...等等
答案 1 :(得分:2)
If I understand your use-case correctly, you can use Cats default Show
type-class or, if you need more custom behaviour, you can directly use generic derivation with Shapeless
to achieve what you are after.
For the easier scenario of Cat's Show
, read here.
As you can read, it has syntax that will let you do myObject.show
on any T
that has a Show[T]
in scope.
If you want a more customisable behaviour, you can try going directly with Shapeless. An example of what you need can be found in this example Show
type-class and you can see it running here.
Given these case 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
It will print:
Bar(i = 0)
BarRec(i = 1, rec = Foo(i = 0, s = foo))
MutualA2(b = MutualB2(b = MutualA1(x = 0)))