1. Overview

In software development, checking if objects have the same class is used for various purposes such as type checking, polymorphism, reflection, error handling, and more.

In this tutorial, we’ll explore several diverse methods for determining whether objects belong to the same class.

2. Dependencies

During this article, we’ll use the kotlin-reflect module, therefore, let’s include it in our pom.xml:

<dependencies>
    <dependency>
        <groupId>org.jetbrains.kotlin</groupId>
        <artifactId>kotlin-reflect</artifactId>
    </dependency>
</dependencies>

Finally, let’s create some simple classes to work with:

open class Weapon

open class Sword : Weapon()

open class Bow : Weapon()

class Claymore : Sword()

class LongBow : Bow()

Notice that all of our classes inherit from Weapon, which gives us several options for class comparisons later.

It’s worth noting that, while we’ve created our own classes for demonstration, the reflection techniques we’ll explore apply to any class we’ll encounter in Kotlin.

3. The Class Literal

With everything imported, we can now look at one of the easiest ways to check if objects are the same class: via their class reference. We’ll use the KClass of our objects, the Kotlin class reference, to check for equality.

Let’s start by writing a simple test. We’ll create two instances of the same class, and we can then obtain the class references with the double-colon operator:

@Test
fun `A sword and a bow are Weapons`() {
    val sword = Weapon()
    val bow = Weapon()

    assertEquals(sword::class, bow::class)
    assertThat(sword::class).isEqualTo(bow::class)

    assertThat(sword).isInstanceOf(Weapon::class.java)
    assertInstanceOf(Weapon::class.java, sword)
}

In the statements above, we’ve verified that both instances have the same class references and that they’re instances of the Weapon class by using methods provided by AssertJ.

The test will pass, thus confirming that sword and bow both reference and are instances of the Weapon class.

4. Accessing and Converting Types

Using the KClass gives us access to multiple properties, allowing us to determine if the created instances share a common base class. We can even use this class reference to access the regular Java class reference.

4.1. Java Class Reference

As mentioned, the Kotlin KClass can be changed to a Java Class at runtime and vice versa. Although they have the same package and name, when these classes are compared, they aren’t equal:

@Test
fun `A Kotlin Weapon class is not a Java Weapon class`() {
    assertEquals("class com.baeldung.compare.kclass.Weapon", Weapon::class.toString())
    assertEquals("class com.baeldung.compare.kclass.Weapon", Weapon::class.java.toString())

    assertNotEquals(Weapon::class, Weapon::class.java)
}

4.2. Converting Between KClass and Class

It’s worth mentioning that when working with instances of different classes, there are several properties we can utilize for comparison. To demonstrate, we’ll use the javaClass and javaClass.kotlin properties:

@Test
fun `Using javaClass and kotlin properties for equality comparison`(){
    val sword = Weapon()
    val bow = Weapon()

    assertEquals("class com.baeldung.compare.kclass.Weapon", sword.javaClass.toString())
    assertEquals("class com.baeldung.compare.kclass.Weapon", bow.javaClass.kotlin.toString())

    assertEquals(sword.javaClass, bow.javaClass)
    assertEquals(sword.javaClass.kotlin, bow.javaClass.kotlin)
}

Internally, when using the javaClass extension property, it calls the getClass() method, which, in turn, returns a Java Class. However, when working with javaClass.kotlin, we’ll receive the Kotlin KClass again.

Just as in the previous example, when we used class.java, the javaClass reference will not be equal to the javaClass.kotlin reference:

@Test
fun `Comparing javaClass and kotlin properties for Weapon class`(){
    val sword = Weapon()
    val bow = Weapon()

    assertNotEquals(sword.javaClass, bow.javaClass.kotlin)
}

5. Handling Inheritance

When working with inheritance, we can use KClass properties and methods for comparing subclasses and superclasses:

@Test
fun `Different weapons indirectly inherit from Weapon class`() {
    val weapons = listOf(Sword(), Claymore(), Bow(), LongBow())

    assertThat(weapons).allMatch { it::class.allSuperclasses.contains(Weapon::class) }
    assertThat(weapons).allMatch { it::class.isSubclassOf(Weapon::class) }

    assertEquals(weapons, weapons.filter { it::class.allSuperclasses.contains(Weapon::class) })
}

Following the above test, we’ve proven that all items of the weapons list inherit from the Weapon class.

To verify the immediate superclass of our weapons list, we use the superclasses property:

@Test
fun `Different weapons directly inherit from Weapon class`() {
    val weapons = listOf(Sword(), Claymore(), Bow(), LongBow())

    assertThat(weapons).anyMatch { it::class.superclasses.contains(Weapon::class) }

    assertNotEquals(weapons, weapons.filter { it::class.superclasses.contains(Weapon::class) })
}

As a result, only Sword and Bow directly inherit from the Weapon class.

Furthermore, we can check the other way around, if the Weapon class is a superclass of any of our weapons:

@Test
fun `Weapon class is a superclass of different weapons`() {
    val weapons = listOf(Sword(), Claymore(), Bow(), LongBow())

    assertThat(weapons).allMatch { Weapon::class.isSuperclassOf(it::class) }
}

6. The is Operator

Using the is operator is fairly straightforward, making it one of the easiest ways to check if objects have the same class at some point in the inheritance tree. We can use this operator to get a boolean value indicating if an object is an instance of a class:

@Test
fun `Different swords are Weapons`() {
    val weapon = Weapon()
    val sword = Sword()
    val claymore = Claymore()

    assertThat(weapon is Weapon).isTrue()
    assertThat(sword is Weapon).isTrue()
    assertThat(claymore is Weapon).isTrue()
}

The is operator in Kotlin is similar to Java’s instanceof keyword for testing the class hierarchy of an object.

7. Conclusion

In this article, we’ve explored multiple ways to compare object classes using several methods.

First, we looked at how the class literal can help us determine if two objects reference the same class.

We then explored other approaches utilizing different class properties, allowing us to compare Java classes to Kotlin classes and vice versa. We also discovered how to determine if classes share common subclasses or superclasses.

Finally, we brought it together with the is operator, enabling us to directly check whether an object references the same class as another, including any inherited classes.

As always, the full implementation of these examples can be found over on GitHub.