https://kotlinlang.org logo
Title
j

JP

05/28/2020, 6:50 AM
What is the
hashCode()
and
toString()
implementation for the cases where I don’t define a class as data class? For example when I examined with a class like this:
class ListNode(var elem: Int) {
    var next: ListNode? = null
}

fun main() {
    val node = ListNode(123)
    println(node.hashCode())  // 895328852
    println(node.toString())  // ListNode@355da254

    node.elem = 456
    println(node.hashCode())  // 895328852
    println(node.toString())  // ListNode@355da254
}
whereas when I examined when changing to data class:
data class ListNode(var elem: Int) {
    var next: ListNode? = null
}

fun main() {
    val node = ListNode(123)
    println(node.hashCode())  // 123
    println(node.toString())  // ListNode(elem=123)

    node.elem = 456
    println(node.hashCode())  // 456
    println(node.toString())  // ListNode(elem=456)
}
From this my assumption is that the
hashCode()
and
toString()
depends on the memory address when the class is not a data class, whereas in case of data class, it will not depend on the memory address but the properties which are defined in the primary constructor (which makes sense, since that would be what data classes are for and how they should behave). Is my assumption correct? And where can I see these default implementations for
hashCode()
, both for class and data class? I wasn’t able to find them.
a

araqnid

05/28/2020, 8:33 AM
Yes, that’s correct. The default implementations for non-data classes are on java.lang.Object (for the JVM platform), although hashCode is a native method. The implementations for data classes are synthesised by the Kotlin compiler, so there is no source for them as such, although you can use the byte code viewer/decompiler supplied with IntelliJ to see them.
👍 1
m

Matteo Mirk

05/28/2020, 8:40 AM
Your assumption is almost correct 🙂 But it hasn’t to do entirely with
data
classes, rather with a class overriding or not such methods. The default implementations are defined in
Any
which is the topmost class in the type hierarchy (similar to Java’s
Object
), but you can’t see them in the source code as they are synthesized by the compiler I guess. As you noted the default hashCode is computed in some way and toString depends on the memory address. When you override those method you can make them return anything you like, although you should respect their contract. For data classes the compiler generates those implementation (along with other methods) which depend on all and only fields declared in the constructor.
👍 1
Finished writing too late, sorry 😛
j

JP

05/28/2020, 12:08 PM
@Matteo Mirk Not at all, thanks for the reply 🙂 Is is still possible inspect these code synthesized by the compiler, by using byte code viewer or decompiler suggested by @araqnid? I’ve just got to know about this feature. I took a look at Oracle’s Java documentation about Object, but there it wasn’t stated how it’s implemented. I also found this answer from stackoverflow https://stackoverflow.com/a/49175508. Would this be valid?
m

Matteo Mirk

05/29/2020, 8:43 AM
You’re welcome! Yes I think it’s possible to disassemble such code, but in general you shouldn’t care about
Any
implementation as many standard lib classes implement their own, and in your classes you’re encouraged to implement toString() if you need a readable or meaningful string representation, and equals/hashcode contract (always together) if your class needs to be used inside collections.
j

JP

05/29/2020, 11:09 AM
I understand. I was simply curious though how those would be implemented, especially the
hashCode()
function.
m

Matteo Mirk

05/29/2020, 12:52 PM
oh sure, to learn more about the hashing algorithm used in Java and Kotlin you can read here for example: https://www.baeldung.com/java-hashcode