1. Overview

在软件开发中,判断两个对象是否属于同一类,常用于类型检查、多态、反射、异常处理等多个场景。

在本教程中,我们将探讨几种在 Kotlin 中判断两个对象是否属于同一类的方法。

2. 依赖配置

本文中,我们会使用到 Kotlin 的反射模块 kotlin-reflect,因此需要在 pom.xml 中添加如下依赖:

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

接着,我们定义一些简单的类用于测试:

open class Weapon

open class Sword : Weapon()

open class Bow : Weapon()

class Claymore : Sword()

class LongBow : Bow()

可以看到,这些类都继承自 Weapon,这为我们后续的类比较提供了多样的继承结构。

当然,这些反射技巧不仅适用于我们自定义的类,也适用于 Kotlin 中的任何类。

3. 使用类字面量(Class Literal)

准备好依赖和类之后,我们可以使用类的引用(KClass)来判断两个对象是否是同一类。这是最直接的方法之一。

我们来写一个简单的测试:

@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)
}

上面的测试使用了 ::class 获取类的 Kotlin 类型引用,并通过 AssertJ 的方法判断是否是某个类的实例。

✅ 该测试会通过,说明 swordbow 都是 Weapon 类的实例。

4. 获取并转换类型信息

KClass 提供了多个属性,可以用来获取类的元信息,甚至可以转换为 Java 的 Class 对象,从而进行跨语言比较。

4.1. Java 类引用

虽然 Kotlin 的 KClass 和 Java 的 Class 表面上看起来一样,但它们并不是同一类型:

@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)
}

⚠️ 注意:Weapon::class 是 Kotlin 类型,Weapon::class.java 是 Java 类型,两者不能直接比较。

4.2. 在 KClass 与 Class 之间转换

我们可以通过 javaClasskotlin 属性进行类型转换:

@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)
}

上述代码中:

  • javaClass 返回 Java 的 Class 实例
  • javaClass.kotlin 返回 Kotlin 的 KClass 实例

但要注意,下面这个测试会失败:

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

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

✅ 所以在进行类型比较时,要确保使用的是相同类型的引用(Kotlin 对 Kotlin,Java 对 Java)。

5. 处理继承关系

当涉及到类的继承结构时,我们可以使用 KClass 提供的一些方法来判断类之间的继承关系。

例如,我们有如下对象列表:

val weapons = listOf(Sword(), Claymore(), Bow(), LongBow())

我们可以判断这些对象是否都继承自 Weapon

@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) })
}

✅ 所有对象都间接继承自 Weapon

如果我们想判断是否直接继承,则可以使用 superclasses 属性:

@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) })
}

✅ 只有 SwordBow 直接继承自 Weapon

反过来,我们也可以判断 Weapon 是否是这些类的父类:

@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. 使用 is 操作符

Kotlin 提供了 is 操作符,用于判断一个对象是否是某个类或其子类的实例,这是最简单也最常用的方式之一。

示例:

@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()
}

✅ 这个操作符在功能上与 Java 的 instanceof 类似,但在语法上更简洁优雅。

⚠️ 踩坑提醒:is 判断的是继承链中的类,不局限于直接类型。如果只想判断精确类型,应使用 ===::class

7. 总结

在本文中,我们介绍了几种在 Kotlin 中判断两个对象是否属于同一类的方法:

  • ✅ 使用 ::class 获取 Kotlin 类型引用,进行精确比较
  • ✅ 通过 javaClasskotlin 属性在 Java 与 Kotlin 类型之间转换
  • ✅ 使用 is 操作符判断是否是某个类或其子类的实例
  • ✅ 利用 allSuperclassessuperclassesisSubclassOfisSuperclassOf 等方法判断继承关系

这些方法各有适用场景,关键在于理解它们之间的差异以及使用场景。

如需查看完整示例代码,欢迎访问 GitHub 项目地址


原始标题:Checking if Two Objects Have the Same Class in Kotlin