Difference Between “==” and “===” Operators in Kotlin

Table of Contents

Introduction

In Kotlin, you may come across two different comparison operators: “==” and “===” when working with conditionals or evaluating equality. Understanding the distinction between these operators is crucial to avoid unexpected behavior in your code. In this article, we’ll explore the difference between “==” and “===” operators in Kotlin and when to use each of them.

The “==” Operator (Equality Operator)

The “==” operator in Kotlin is used to check if two values are equal in terms of their content or data. When comparing non-primitive types, such as objects, the “==” operator checks for structural equality, meaning it compares the contents of the objects rather than their memory addresses.

Let’s look at an example:

fun main() {
    val num1 = 10
    val num2 = 10

    val list1 = listOf(1, 2, 3)
    val list2 = listOf(1, 2, 3)

    println(num1 == num2) // Output: true
    println(list1 == list2) // Output: true
}

In the above example, the “==” operator checks if the values of num1 and num2, as well as the contents of list1 and list2, are equal. It returns true in both cases.

The “===” Operator (Identity Operator)

The “===” operator in Kotlin is used to check if two references point to the same object in memory, regardless of their content. It tests for referential equality, ensuring that both operands reference the same instance in memory.

Take a look at this example:

fun main() {
    val list1 = listOf(1, 2, 3)
    val list2 = listOf(1, 2, 3)

    println(list1 === list2) // Output: false
}

In this example, the “===” operator compares the references of list1 and list2. Since they are separate instances in memory (even though their contents are the same), the result is false.

Use Cases for “==” and “===” Operators

  • Use “==” when you want to compare the contents of variables, especially for non-primitive types like objects or lists. This is the most common way to check equality.
  • Use “===” when you specifically want to check if two references point to the same object in memory. This is useful when you need to ensure that two variables are referring to the exact same instance.

Caveats and Considerations

  1. For primitive types (e.g., Int, Double, Char), the “==” and “===” operators behave the same way, as they are compared by their content in memory.
  2. For custom classes, you can override the equals() method to define your own content-based comparison behavior, which will be used when using the “==” operator.
  3. When comparing nullable types, such as String? or Any?, using the “===” operator will check for the exact same instance (identity), while the “==” operator will evaluate their content only if both are non-null. If either operand is null, the “==” operator will return false.

Overriding the “equals” Method

As mentioned earlier, you can override the equals() method in your custom classes to provide a content-based comparison when using the “==” operator. By default, the equals() method checks for referential equality, similar to the “===” operator. However, by overriding it, you can define your own logic for determining equality.

Let’s create a simple class Person to demonstrate this:

data class Person(val name: String, val age: Int)

In this example, we’ve used the data keyword before the class declaration, which automatically generates equals(), hashCode(), and toString() methods for us.

Now, let’s override the equals() method to consider two Person objects equal if their names and ages are the same:

data class Person(val name: String, val age: Int) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is Person) return false

        return this.name == other.name && this.age == other.age
    }

    // Additional code (hashCode(), toString(), etc.) can go here if needed
}

With the equals() method overridden, the “==” operator will now perform content-based comparison for Person objects:

fun main() {
    val person1 = Person("Alice", 30)
    val person2 = Person("Alice", 30)
    val person3 = Person("Bob", 25)

    println(person1 == person2) // Output: true
    println(person1 == person3) // Output: false
}

Handling Nullable Types

When dealing with nullable types, it’s essential to be aware of how the “==” and “===” operators behave.

Consider the following example:

fun main() {
    val name1: String? = "John"
    val name2: String? = "John"

    println(name1 == name2) // Output: true
    println(name1 === name2) // Output: true

    val name3: String? = null
    val name4: String? = null

    println(name3 == name4) // Output: true
    println(name3 === name4) // Output: true
}

In this example, the “==” operator returns true for both non-null and nullable variables if their content is the same. However, the “===” operator returns true only for non-null variables because it checks for referential equality.

When one or both of the variables are nullable and not equal to each other, the “==” operator will return false, but the “===” operator will still return false only if both variables are non-null.

Caveats with Primitive Types

For primitive types like Int, Double, Char, etc., the “==” and “===” operators behave the same way, as they are compared by their content in memory. However, this is not the case with custom classes or non-primitive types.

Conclusion

Understanding the difference between the “==” and “===” operators in Kotlin is essential for performing accurate comparisons in your code. The “==” operator is used for content-based comparison, while the “===” operator is used for referential equality, checking if two references point to the same object in memory.

By overriding the equals() method in custom classes, you can control how content-based comparisons are performed using the “==” operator. Additionally, be mindful of nullable types and their behavior with these operators to avoid unexpected results in your Kotlin applications.

Command PATH Security in Go

Command PATH Security in Go

In the realm of software development, security is paramount. Whether you’re building a small utility or a large-scale application, ensuring that your code is robust

Read More »
Undefined vs Null in JavaScript

Undefined vs Null in JavaScript

JavaScript, as a dynamically-typed language, provides two distinct primitive values to represent the absence of a meaningful value: undefined and null. Although they might seem

Read More »