Any?.toString() problem

I love Kotlin, but there’s a huge problem that constantly haunts me whenever I try to use Any?.toString() - I don’t know what exactly current implementation of this function returns. I always ask myself these questions: “was it untouched?”, “was it overridden? what does it return if so?”. I myself also cautiously override this function because it can be easily misinterpreted by other developers or they may not be able to find out that function was indeed overridden and is safe to use.

For example, let’s see kotlinx-serialization-json JsonPrimitive.toString() implementation. It literally returns "foo" (incl. quotes) if the field was of type String, but we don’t have a convenient way to get String without quotes from this JsonElement if it was a field of different type. In my case, I used JsonElement.toString in custom Serializer<T> to determine the type of json, so this thing was causing runtime exception because I didn’t except it to come with quotes
So, I created a workaround:

internal val JsonPrimitive.stringOrNull: String?
    get() {
        val inputString = this.toString()
        val formatted = inputString.removeSurrounding("\"")

        return if (formatted == inputString) null else formatted
    }

But I don’t think that these “workarounds” are good replacements.

I just want there to be some kind of separation between default String representation of a class and toString() that converts fields in a nice developer-intended String

I understand that toString() problem comes from Java, but why would you not fix that in Kotlin?

What would be the “fix”? How should this be implemented?
Kotlin isn’t Rust, unfortunately, where you can define common behavior using traits.

A “toString() problem”? Every language I know has some way of printing an object. I guess it could be miss-used but that’s true for all methods.

It’s rare for a library to use toString inconsistently while also making toString a heavily used method. Of course, this isn’t a problem for just toString–a library author could use content() inconsistently and unclearly.

If you ever find that you want toString to behave with a specific contract, make sure you aren’t making that assumption without a good hint from the author. The author will hopefully have made that very clear that toString obeys X, Y, Z. By default, assume the behavior isn’t a stong public API

Have you looked at the libraries and searched for other methods? Usually, they will have some other way of accessing the content. Sometimes it requires refining the type into a more specific value (i.e. JsonString perhaps?). There’s a good chance you’re not accessing the value in the intended way.

Have you looked at content? It does exactly what you want:

Content of given element without quotes. For JsonNull this methods returns null

I’d suggest there is no general “toString problem”, just a clarity/usage issue.

1 Like

Have you looked at content? It does exactly what you want

Oh, that’s exatly what I wanted. Thanks. Don’t know why I didn’t see this property when searched the docs myself. I made an assumption that if there are int/intOrNull, boolean/booleanOrNull properties there also should be something like string/stringOrNull and used toString.

But this problem occured to me in other cases too.

I’d suggest there is no general “toString problem”, just a clarity/usage issue

Yeah, that’s what I tried to say. But I also don’t know how to make it more clear. In perfect world every custom override would be documented, but often it’s not the case

Kotlin docs maybe don’t explain this clearly, because toString() came from Java, but this function generally isn’t suitable for anything else than printing, debugging, etc. We should not use it to acquire any kind of data from an object and then process this data. In my life it happened a few times that a library author assumed we actually should use toString() to acquire some very specific data, but it always felt as I do something wrong.

3 Likes