Given a case class that - unfortunately - overrides the toString method, any way to bypass that method?
I.e:
case class Foo(s:String){
override def toString = s
}
Then
val foo = Foo("Hello World")
println(foo)
will yield
Hello World
If I am just given foo
(but not Foo
) can I do anything to foo
so that it will print
Foo(Hello World)
instead of just the string?
Using a Show
type class as shown by 0__ is a good option. Unfortunately toString
is so prevalent (a lot of code relies on it to format a string given an object) that there are a lot of case where a type class won't you do any good. By example, if you have a List[Foo]
, calling toString
on it will call Foo.toString
. The right solution using a type class will be to define an instance for lists, which will itself call show
on the Foo
instances. But this only pushes the problem further away, because it might very well happen that you are passing this list to some third party code that you have no control on, and that will call List.toString
(instead of Show.show
). In this specific case, the only viable solution is to wrap Foo
class in your very own class (say MyFoo
) and override toString
there. Obviously, this will only be useful if you can change your List|[Foo]
into a List[MyFoo]
implicit class MyFoo(val foo: Foo) extends AnyVal {
override def toString = s"Foo(${foo.s})"
}
object MyFoo {
implicit def unwrap(myFoo: MyFoo): Foo = myFoo.foo
}
Repl test:
scala> val list1: List[Foo] = List(Foo("foo"), Foo("bar"))
list1: List[Foo] = List(foo, bar)
scala> val list2: List[MyFoo] = List(Foo("foo"), Foo("bar"))
list2: List[MyFoo] = List(Foo(foo), Foo(bar))
As you can see, the string representation of list2
uses you very own toString
implementation.
Clearly, this solution is not ideal. It might even be totally impractical in many situations (by example, you cannot pass a MyFoo
to a method expecting a Foo
). If anything, it shows that relying on a toString
method slapped right in Any
is not the best design, but alas we have to live and work with a lot of code (including the whole java ecosystem) that does just that.
So as clunky as the above solution is, if you don't have control over who will call Foo.toString
(meaning that you cannot change that call to anything else, such as Show.show
) you might not be able to do much better.