Structural and Referential Equality
Structural and Referential Equality
Kotlin provides two distinct operators for comparing objects: structural equality (==) and referential equality (===). Structural equality checks whether two objects have equivalent content, while referential equality checks whether two references point to the exact same instance in memory. Understanding when to use each is essential for correct comparisons in collections, caching, and identity checks. By the end of this lesson, you will be able to:
- Explain the difference between structural equality and referential equality.
- Describe how the
==operator delegates to theequals()method. - Identify when referential equality is necessary instead of structural equality.
- Explain how data classes generate
equals()andhashCode()automatically.
Structural Equality with ==
The == operator in Kotlin translates to a call to the equals() method. If the left operand is null, the operator returns true only when the right operand is also null, without calling equals(). Otherwise, it calls left.equals(right):
data class Person(val name: String, val age: Int)
val person1 = Person("skydoves", 30)
val person2 = Person("skydoves", 30)
println(person1 == person2) // true
Because Person is a data class, the compiler generates an equals() implementation that compares all properties declared in the primary constructor. Two Person instances with the same name and age are structurally equal even though they are separate objects in memory.
For regular classes that do not override equals(), the default implementation from Any falls back to referential equality. This means == and === behave identically for classes that do not provide a custom equals().
Referential Equality with ===
The === operator checks whether two references point to the same object in memory. It does not call any method and cannot be overridden:
val person1 = Person("skydoves", 30)
val person2 = Person("skydoves", 30)
val person3 = person1
println(person1 === person2) // false
println(person1 === person3) // true
person1 and person2 have identical content but are different instances, so === returns false. person3 is assigned the same reference as person1, so === returns true.
Referential equality is useful when you need to verify identity rather than content. For example, checking whether a callback lambda is the same instance you registered earlier, or verifying that a cached object has not been replaced.
Negated Equality
Kotlin provides != for negated structural equality and !== for negated referential equality. These follow the same rules as their positive counterparts:
This interview continues for subscribers
Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.
Become a Sponsor