stringkotlinoperator-overloadingdollar-sign

How do I overload Kotlin's string template operator (the dollar symbol)?


I'm sure I've read that Kotlin's "dollar operator" for string templates can be redefined, but their documentation on operator overloading does not indicate how to overload it, and their documentation on string templates, like most online tutorials on string templates, tell you merely how to use the dollar symbol. A search on StackOverflow likewise turned up no obvious questions that address this issue.

Is there a way to overload the Kotlin string template operator, AKA the dollar symbol?

For an example:

data class Point(x: Int, y: Int)
data class Stick(val p: Point, val q: Point)
console.log('${ Stick( Point(2,1) , Point(1,7) ) }')

The output is

Stick(p=Point(x=2, y=1), q=Point(x=1, y=7))

But I don't want that. I want

[ ( 2 , 1 ) ; ( 1 , 7 ) ]

How do I do that?


Solution

  • You can't overload the $ operator in string templates.

    However, that simply gets the value of the object or expression; the string template them converts each object into a string by calling its toString() method.  Every object implements that method — it's defined in the Any type that every object inherits from.  (And there's also an extension method so that it works for null too.)

    So you can simply override toString(); that will take effect whenever the object is used in a string template — and also everywhere else it gets converted to a string (e.g. if passed directly to println() or to a logger).

    In this case, I think it'd be more flexible to provide your own toString() implementation for both of your objects, e.g.:

    data class Point(val x: Int, val y: Int) {
        override fun toString() = "( $x , $y )"
    }
    
    data class Stick(val p: Point, val q: Point) {
        override fun toString() = "[ $p ; $q ]"
    }
    

    Providing your own toString() is one way of making your objects more generally useful.  The standard implementation that a data class gives you is clear and readable, but quite long-winded, so it's quite common to give your own.  (And for classes that are not data classes, the implementation inherited from Any is pretty cryptic — e.g. Stick@60652518 — so overriding it is even more important!)

    Generally speaking, toString() tends to be aimed at programmers (e.g. to be shown in logs).  So if that might differ from what you want to show to your users, then you might want to add a separate method for the latter (especially if that would need to show only part of the data, or do more processing such as translating it into the user's language).