I am trying to check if a type conforms to a another type with an if expression like so:
if (String::class is Any::class)
This gives me the error class literals with empty left hand side are not yet supported. Can anyone elaborate on that error and/or tell me how I should be doing this check?
edit (clarification): I can't do an equality check because I need to know if the class on the left either matches the class on the right or is a subclass of it. So if an instance of the class on the left can be safely cast to the class on the right.
Basically I need the equivalent of:
if ("A string" is Any)
But without having a String instance, String just being used an example here.
I guess it wouldn't be clear if Kotlin used the is
operator differently between a KClass
and another KClass
as it does between an instance and a type which is why what I was trying to do doesn't work. Anyway I made this little infix function to imitate the functionality. However it only works with JVM target of course since it's using Java reflection. This is going off of the answer given in this SO post.
infix fun <T : Any, C : Any> KClass<T>.can(comparate: KClass<C>) =
comparate.java.isAssignableFrom(this.java)
This will allow you to do exactly what I was trying to do but with the can
function instead of the is operator like so:
if(String::class can Any::class)
Your error message is that the is
check expects a class name and not a reference to a KClass on the right side. The message itself might be a little unclear. But the same applies in Java, you would not use instanceOf
operator but instead would call isAssignableFrom
.
For help on solving the problem, you have examples that can be found in Github...
In the Klutter library are examples of a lot of combinations of instanceOf style checking between Class
, KClass
, Type
and KType
as well as primitives. You can copy ideas from there. There are many combinations that you might want to have covered in the long run.
Here is a sampling of a big mix of extensions for checking if one type is assignable from the other. A few examples are:
fun <T : Any, O : Any> KClass<T>.isAssignableFrom(other: KClass<O>): Boolean {
if (this.java == other.java) return true
return this.java.isAssignableFrom(other.java)
}
fun <T : Any> KClass<T>.isAssignableFrom(other: Class<*>): Boolean {
if (this.java == other) return true
return this.java.isAssignableFrom(other)
}
fun KClass<*>.isAssignableFromOrSamePrimitive(other: KType): Boolean {
return (this.java as Type).isAssignableFromOrSamePrimitive(other.javaType)
}
fun KClass<*>.isAssignableFromOrSamePrimitive(other: Type): Boolean {
return (this.java as Type).isAssignableFromOrSamePrimitive(other)
}
fun Type.isAssignableFromOrSamePrimitive(other: Type): Boolean {
if (this == other) return true
if (this is Class<*>) {
if (other is Class<*>) {
return this == other.kotlin.javaObjectType || this == other.kotlin.javaPrimitiveType ||
this.isAssignableFrom(other)
}
return this.isAssignableFrom(other.erasedType())
}
return this.erasedType().isAssignableFrom(other.erasedType())
}
// ... and so on for every permutation of types
See the linked source for all permutations.
And you will need this erasedType()
extension used by the above samples -- which goes from a Type
back to a Class
(after type erasure):
@Suppress("UNCHECKED_CAST") fun Type.erasedType(): Class<Any> {
return when (this) {
is Class<*> -> this as Class<Any>
is ParameterizedType -> this.getRawType().erasedType()
is GenericArrayType -> {
// getting the array type is a bit trickier
val elementType = this.getGenericComponentType().erasedType()
val testArray = java.lang.reflect.Array.newInstance(elementType, 0)
testArray.javaClass
}
is TypeVariable<*> -> {
// not sure yet
throw IllegalStateException("Not sure what to do here yet")
}
is WildcardType -> {
this.getUpperBounds()[0].erasedType()
}
else -> throw IllegalStateException("Should not get here.")
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With