Reflection: add KClass.{isSubclassOf,isSuperclassOf}, KType.{isSubtypeOf,isSupertypeOf}
The behavior in primitives.kt is likely to be reconsidered #KT-8998 Fixed
This commit is contained in:
+48
@@ -0,0 +1,48 @@
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.*
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.assertFalse
|
||||
|
||||
open class Klass
|
||||
interface Interface<T>
|
||||
class Bar : Interface<String>, Klass()
|
||||
|
||||
fun check(subclass: KClass<*>, superclass: KClass<*>, shouldBeSubclass: Boolean) {
|
||||
if (shouldBeSubclass) {
|
||||
assertTrue(subclass.isSubclassOf(superclass))
|
||||
assertTrue(superclass.isSuperclassOf(subclass))
|
||||
} else {
|
||||
assertFalse(subclass.isSubclassOf(superclass))
|
||||
assertFalse(superclass.isSuperclassOf(subclass))
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
check(Any::class, Any::class, true)
|
||||
check(String::class, Any::class, true)
|
||||
check(Any::class, String::class, false)
|
||||
check(String::class, String::class, true)
|
||||
|
||||
check(Int::class, Int::class, true)
|
||||
check(Int::class, Any::class, true)
|
||||
|
||||
check(List::class, Collection::class, true)
|
||||
check(List::class, Iterable::class, true)
|
||||
check(Collection::class, Iterable::class, true)
|
||||
check(Set::class, List::class, false)
|
||||
|
||||
check(Array<String>::class, Array<Any>::class, false)
|
||||
check(Array<Any>::class, Array<String>::class, false)
|
||||
|
||||
check(Function3::class, Function4::class, false)
|
||||
check(Function4::class, Function3::class, false)
|
||||
|
||||
check(Bar::class, Klass::class, true)
|
||||
check(Bar::class, Interface::class, true)
|
||||
check(Klass::class, Bar::class, false)
|
||||
check(Interface::class, Bar::class, false)
|
||||
check(Klass::class, Interface::class, false)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.isSubclassOf
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.assertFalse
|
||||
|
||||
fun box(): String {
|
||||
// KClass instances for primitive int and wrapper java.lang.Integer are different
|
||||
val primitiveInt = Int::class.javaPrimitiveType!!.kotlin
|
||||
val wrapperInt = Int::class.javaObjectType.kotlin
|
||||
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))
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// WITH_REFLECT
|
||||
// FILE: J.java
|
||||
|
||||
public interface J {
|
||||
String platformString();
|
||||
}
|
||||
|
||||
// FILE: K.kt
|
||||
|
||||
import kotlin.reflect.*
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
fun string(): String = null!!
|
||||
fun nullableString(): String? = null!!
|
||||
|
||||
fun check(subCallable: KCallable<*>, superCallable: KCallable<*>) {
|
||||
val subtype = subCallable.returnType
|
||||
val supertype = superCallable.returnType
|
||||
assertTrue(subtype.isSubtypeOf(supertype))
|
||||
assertTrue(supertype.isSupertypeOf(subtype))
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
check(::string, J::platformString)
|
||||
check(J::platformString, ::string)
|
||||
check(::nullableString, J::platformString)
|
||||
check(J::platformString, ::nullableString)
|
||||
check(J::platformString, J::platformString)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.*
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.assertFalse
|
||||
|
||||
open class G<T>
|
||||
class A : G<String>()
|
||||
|
||||
fun gOfString(): G<String> = null!!
|
||||
fun gOfInt(): G<Int> = null!!
|
||||
|
||||
fun box(): String {
|
||||
val gs = ::gOfString.returnType
|
||||
val gi = ::gOfInt.returnType
|
||||
val a = ::A.returnType
|
||||
|
||||
assertTrue(a.isSubtypeOf(gs))
|
||||
assertTrue(gs.isSupertypeOf(a))
|
||||
|
||||
assertFalse(a.isSubtypeOf(gi))
|
||||
assertFalse(gi.isSupertypeOf(a))
|
||||
|
||||
assertFalse(gs.isSubtypeOf(gi))
|
||||
assertFalse(gs.isSupertypeOf(gi))
|
||||
assertFalse(gi.isSubtypeOf(gs))
|
||||
assertFalse(gi.isSupertypeOf(gs))
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.*
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.assertFalse
|
||||
|
||||
fun check(subCallable: KCallable<*>, superCallable: KCallable<*>, shouldBeSubtype: Boolean) {
|
||||
val subtype = subCallable.returnType
|
||||
val supertype = superCallable.returnType
|
||||
if (shouldBeSubtype) {
|
||||
assertTrue(subtype.isSubtypeOf(supertype))
|
||||
assertTrue(supertype.isSupertypeOf(subtype))
|
||||
} else {
|
||||
assertFalse(subtype.isSubtypeOf(supertype))
|
||||
assertFalse(supertype.isSupertypeOf(subtype))
|
||||
}
|
||||
}
|
||||
|
||||
open class O
|
||||
class X : O()
|
||||
|
||||
fun any(): Any = null!!
|
||||
fun string(): String = null!!
|
||||
fun nullableString(): String? = null!!
|
||||
fun int(): Int = null!!
|
||||
fun nothing(): Nothing = null!!
|
||||
fun nullableNothing(): Nothing? = null!!
|
||||
fun function2(): (Any, Any) -> Any = null!!
|
||||
fun function3(): (Any, Any, Any) -> Any = null!!
|
||||
|
||||
fun box(): String {
|
||||
check(::any, ::any, true)
|
||||
check(::int, ::int, true)
|
||||
check(::nothing, ::nothing, true)
|
||||
check(::nullableNothing, ::nullableNothing, true)
|
||||
|
||||
check(::string, ::any, true)
|
||||
check(::nullableString, ::any, false)
|
||||
check(::int, ::any, true)
|
||||
check(::O, ::any, true)
|
||||
check(::X, ::any, true)
|
||||
|
||||
check(::nothing, ::any, true)
|
||||
check(::nothing, ::string, true)
|
||||
check(::nothing, ::nullableString, true)
|
||||
check(::nullableNothing, ::nullableString, true)
|
||||
check(::nullableNothing, ::string, false)
|
||||
|
||||
check(::string, ::nullableString, true)
|
||||
check(::nullableString, ::string, false)
|
||||
|
||||
check(::X, ::O, true)
|
||||
check(::O, ::X, false)
|
||||
|
||||
check(::int, ::string, false)
|
||||
check(::string, ::int, false)
|
||||
check(::any, ::string, false)
|
||||
check(::any, ::nullableString, false)
|
||||
|
||||
check(::function2, ::function3, false)
|
||||
check(::function3, ::function2, false)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.isSubtypeOf
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.assertFalse
|
||||
|
||||
class G<T>
|
||||
|
||||
fun number(): G<Number> = null!!
|
||||
fun outNumber(): G<out Number> = null!!
|
||||
fun inNumber(): G<in Number> = null!!
|
||||
fun star(): G<*> = null!!
|
||||
|
||||
fun box(): String {
|
||||
val n = ::number.returnType
|
||||
val o = ::outNumber.returnType
|
||||
val i = ::inNumber.returnType
|
||||
val st = ::star.returnType
|
||||
|
||||
// G<Number> <: G<out Number>
|
||||
assertTrue(n.isSubtypeOf(o))
|
||||
assertFalse(o.isSubtypeOf(n))
|
||||
|
||||
// G<Number> <: G<in Number>
|
||||
assertTrue(n.isSubtypeOf(i))
|
||||
assertFalse(i.isSubtypeOf(n))
|
||||
|
||||
// G<Number> <: G<*>
|
||||
assertTrue(n.isSubtypeOf(st))
|
||||
assertFalse(st.isSubtypeOf(n))
|
||||
|
||||
// G<out Number> <: G<*>
|
||||
assertTrue(o.isSubtypeOf(st))
|
||||
assertFalse(st.isSubtypeOf(o))
|
||||
|
||||
// G<in Number> <: G<*>
|
||||
assertTrue(i.isSubtypeOf(st))
|
||||
assertFalse(st.isSubtypeOf(i))
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -12993,6 +12993,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("isSubclassOfIsSuperclassOf.kt")
|
||||
public void testIsSubclassOfIsSuperclassOf() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/supertypes/isSubclassOfIsSuperclassOf.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("primitives.kt")
|
||||
public void testPrimitives() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/supertypes/primitives.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleSupertypes.kt")
|
||||
public void testSimpleSupertypes() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/supertypes/simpleSupertypes.kt");
|
||||
@@ -13139,6 +13151,39 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/reflection/types/subtyping")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Subtyping extends AbstractBlackBoxCodegenTest {
|
||||
public void testAllFilesPresentInSubtyping() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/types/subtyping"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("platformType.kt")
|
||||
public void testPlatformType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/types/subtyping/platformType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleGenericTypes.kt")
|
||||
public void testSimpleGenericTypes() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/types/subtyping/simpleGenericTypes.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleSubtypeSupertype.kt")
|
||||
public void testSimpleSubtypeSupertype() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/types/subtyping/simpleSubtypeSupertype.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("typeProjection.kt")
|
||||
public void testTypeProjection() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/types/subtyping/typeProjection.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -223,3 +223,16 @@ val KClass<*>.allSuperclasses: Collection<KClass<*>>
|
||||
get() = allSupertypes.map { supertype ->
|
||||
supertype.classifier as? KClass<*> ?: throw KotlinReflectionInternalError("Supertype not a class: $supertype")
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if `this` class is the same or is a (possibly indirect) subclass of [base], `false` otherwise.
|
||||
*/
|
||||
fun KClass<*>.isSubclassOf(base: KClass<*>): Boolean =
|
||||
this == base ||
|
||||
DFS.ifAny(listOf(this), KClass<*>::superclasses) { it == base }
|
||||
|
||||
/**
|
||||
* Returns `true` if `this` class is the same or is a (possibly indirect) superclass of [derived], `false` otherwise.
|
||||
*/
|
||||
fun KClass<*>.isSuperclassOf(derived: KClass<*>): Boolean =
|
||||
derived.isSubclassOf(this)
|
||||
|
||||
@@ -19,6 +19,7 @@ package kotlin.reflect
|
||||
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import kotlin.reflect.jvm.internal.KTypeImpl
|
||||
|
||||
/**
|
||||
@@ -35,3 +36,18 @@ fun KType.withNullability(nullable: Boolean): KType {
|
||||
|
||||
return if (!nullable) this else KTypeImpl(TypeUtils.makeNullable(kotlinType)) { javaType }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns `true` if `this` type is the same or is a subtype of [other], `false` otherwise.
|
||||
*/
|
||||
fun KType.isSubtypeOf(other: KType): Boolean {
|
||||
return (this as KTypeImpl).type.isSubtypeOf((other as KTypeImpl).type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if `this` type is the same or is a supertype of [other], `false` otherwise.
|
||||
*/
|
||||
fun KType.isSupertypeOf(other: KType): Boolean {
|
||||
return other.isSubtypeOf(this)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user