diff --git a/compiler/testData/codegen/box/classLiteral/primitiveKClassEquality.kt b/compiler/testData/codegen/box/classLiteral/primitiveKClassEquality.kt new file mode 100644 index 00000000000..21ca908c6ed --- /dev/null +++ b/compiler/testData/codegen/box/classLiteral/primitiveKClassEquality.kt @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +import kotlin.test.assertEquals +import kotlin.test.assertFalse + +fun box(): String { + val x = Int::class.javaPrimitiveType!!.kotlin + val y = Int::class.javaObjectType.kotlin + + assertEquals(x, y) + assertEquals(x.hashCode(), y.hashCode()) + assertFalse(x === y) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/classes/primitiveKClassEquality.kt b/compiler/testData/codegen/box/reflection/classes/primitiveKClassEquality.kt new file mode 100644 index 00000000000..9a5ab6b07fd --- /dev/null +++ b/compiler/testData/codegen/box/reflection/classes/primitiveKClassEquality.kt @@ -0,0 +1,15 @@ +// WITH_REFLECT + +import kotlin.test.assertEquals +import kotlin.test.assertFalse + +fun box(): String { + val x = Int::class.javaPrimitiveType!!.kotlin + val y = Int::class.javaObjectType.kotlin + + assertEquals(x, y) + assertEquals(x.hashCode(), y.hashCode()) + assertFalse(x === y) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/supertypes/primitives.kt b/compiler/testData/codegen/box/reflection/supertypes/primitives.kt index 2d54b7ae8aa..8e3b4764ba5 100644 --- a/compiler/testData/codegen/box/reflection/supertypes/primitives.kt +++ b/compiler/testData/codegen/box/reflection/supertypes/primitives.kt @@ -11,10 +11,9 @@ fun box(): String { assertTrue(primitiveInt.isSubclassOf(primitiveInt)) assertTrue(wrapperInt.isSubclassOf(wrapperInt)) - // Currently KClass for int != KClass for java.lang.Integer, thus they are not a subclass of each other either - // TODO: reconsider this decision - assertFalse(primitiveInt.isSubclassOf(wrapperInt)) - assertFalse(wrapperInt.isSubclassOf(primitiveInt)) + // KClass for int equals KClass for java.lang.Integer, so they are also a subclass of each other + assertTrue(primitiveInt.isSubclassOf(wrapperInt)) + assertTrue(wrapperInt.isSubclassOf(primitiveInt)) return "OK" } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 0410c7dff26..ebb671bfbbc 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -2356,6 +2356,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/classLiteral"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("primitiveKClassEquality.kt") + public void testPrimitiveKClassEquality() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classLiteral/primitiveKClassEquality.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/codegen/box/classLiteral/bound") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -12003,6 +12009,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("primitiveKClassEquality.kt") + public void testPrimitiveKClassEquality() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/classes/primitiveKClassEquality.kt"); + doTest(fileName); + } + @TestMetadata("qualifiedName.kt") public void testQualifiedName() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/classes/qualifiedName.kt"); diff --git a/core/builtins/src/kotlin/reflect/KClass.kt b/core/builtins/src/kotlin/reflect/KClass.kt index 46590f103f5..e4bb27d11c1 100644 --- a/core/builtins/src/kotlin/reflect/KClass.kt +++ b/core/builtins/src/kotlin/reflect/KClass.kt @@ -123,15 +123,11 @@ public interface KClass : KDeclarationContainer, KAnnotatedElement, KCl /** * Returns `true` if [other] is a [KClass] instance representing the same class on a given platform. + * On JVM this means that all of the following conditions are satisfied: * - * On JVM this means that the given instance is backed by the same [Class] object as this one. In particular, it requires - * that the two classes are loaded with the same class loader and have the same name. Note that there are cases where the behavior - * of this method may seem unintuitive: - * * For each JVM primitive type, there are two classes at runtime: one for the primitive itself, and another for the wrapper class. - * [KClass] instances for those classes are different: [KClass] for `int` is **not equal** to [KClass] for `java.lang.Integer`, - * although both have the same qualified name [kotlin.Int]. - * * For JVM arrays of different types, [KClass] instances are different, - * although all of them have the same qualified name [kotlin.Array]. + * 1. [other] has the same fully qualified name as this instance. + * 2. [other]'s backing [Class] object is loaded with the same class loader as the [Class] object of this instance. + * 3. If the classes represent [Array], then [Class] objects of their element types are equal. */ override fun equals(other: Any?): Boolean diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt index 790cd71606c..de30d5054ad 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -196,10 +196,10 @@ internal class KClassImpl(override val jClass: Class) : get() = descriptor.isCompanionObject override fun equals(other: Any?): Boolean = - other is KClassImpl<*> && jClass == other.jClass + other is KClassImpl<*> && javaObjectType == other.javaObjectType override fun hashCode(): Int = - jClass.hashCode() + javaObjectType.hashCode() override fun toString(): String { return "class " + classId.let { classId -> diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt b/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt index fa9a750e150..0820f000f4f 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt +++ b/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt @@ -75,10 +75,10 @@ class ClassReference(override val jClass: Class<*>) : KClass, ClassBasedDec private fun error(): Nothing = throw KotlinReflectionNotSupportedError() override fun equals(other: Any?) = - other is ClassReference && jClass == other.jClass + other is ClassReference && javaObjectType == other.javaObjectType override fun hashCode() = - jClass.hashCode() + javaObjectType.hashCode() override fun toString() = jClass.toString() + Reflection.REFLECTION_NOT_AVAILABLE