Incompatible types: A and kotlin.reflect.KType - kotlin

I want to check that function has parameter of A type use the following code:
import kotlin.reflect.*
import javafx.event.ActionEvent
interface IA {}
class A {}
class B {
fun test(a: A, ia: IA, event: ActionEvent) {
println(a)
println(ia)
println(event)
}
}
fun main(args: Array<String>) {
for (function in B::class.declaredMemberFunctions) {
for (parameter in function.parameters) {
when (parameter.type) {
is IA -> println("Has IA interface parameter.")
is ActionEvent -> println("Has ActionEvent class parameter.")
is A -> println("Has A class parameter.") // <---- compilation error in this line
}
}
}
}
but when I try to compile it I see the following error:
> Error:(20, 19) Incompatible types: A and kotlin.reflect.KType
Questions:
Why compiler don't raise error for IA interface and ActionEvent Java class?
Why compiller raise error for A class?
How to check that function has argument of A type?

The thing is you're trying to check if KType is A, which is always false. And the compiler knows it and raises a compilation error. But IA is an interface a class that implements KType can possibly implement this interface too so there's no compilation error. ActionEvent is an open class so it's subtype can implement KType - no compilation error either.
What you should do to check if the parameter type is some class or some interface is the following.
when (parameter.type.javaType) {
IA::class.javaClass -> println("Has IA interface parameter.")
ActionEvent::class.javaClass -> println("Has ActionEvent class parameter.")
A::class.javaClass -> println("Has A class parameter.")
}

First, you are using the wrong operator. is checks if an instance of something is a given class. You have no instance. You instead have a KType that you are trying to check if it is the instance of a class A, or IA or ActionEvent, it isn't.
So you need to use a different operator, which is to check if they are equal or call the method isAssignableFrom(). And then you need to check that the two things you are comparing are the right datatypes and do what you expect.
In another answer, #Michael says you can just treat a Type and Class the same for equality, that isn't always true; not that simple. Sometimes a Type is a Class but sometimes it is a ParameterizedType, GenericArrayType, TypeVariable, or WildcardType which are not comparable with equals. So that approach is wrong if you ever have a parameter to the method that uses generics it breaks.
Here is a version that does not support generics in that if generics are used in the parameter, they will not match. This also compares KTypeusing equality, which means it does not work for inherited classes matching against a superclass or interface. But the most simple is:
when (parameter.type) {
IA::class.defaultType -> println("Has IA interface parameter.")
ActionEvent::class.defaultType -> println("Has ActionEvent class parameter.")
A::class.defaultType -> println("Has A class parameter.")
}
This breaks if For example if class A had generic parameter T so you wanted to check a parameter that is A<String> or A<Monkey> you will not match A::class.defaultType (FALSE!!!). Or if you tried to compare array types, again will not match.
To fix this generics problem, we need to also erase the paramter.type you are checking. We need a helper function to do that.
Here is one copied from the Klutter library that takes a KType and erases the generics to make a KClass. You will need the kotlin-reflect dependency to use this code. You can remove the kotlin-refect dependency by only working with Java Class and not using KClass anywhere directly. Some other code will have to change.
With the following extension function:
fun KType.erasedType(): KClass<*> {
return this.javaType.erasedType().kotlin
}
#Suppress("UNCHECKED_CAST")
fun Type.erasedType(): Class<*> {
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.")
}
}
You can now write your code more simply as:
when (parameter.type.erasedType()) {
IA::class-> println("Has IA interface parameter.")
ActionEvent::class -> println("Has ActionEvent class parameter.")
A::class -> println("Has A class parameter.")
}
So generics are ignored and this works comparing the raw erased class against each other; but again without inheritance.
To support inheritance you can use this version slightly modified. You need a different form of when expression and a helper function:
fun assignCheck(ancestor: KClass<*>, checkType: KType): Boolean =
ancestor.java.isAssignableFrom(checkType.javaType.erasedType())
Then the when expression changed to:
when {
assignCheck(IA::class, parameter.type) -> println("Has IA interface parameter.")
assignCheck(ActionEvent::class, parameter.type) -> println("Has ActionEvent class parameter.")
assignCheck(A::class, parameter.type) -> println("Has A class parameter.")
}
Extra credit, comparing full generics:
To compare full generics we need to convert everything to something that we can compare that still has generics. The easiest is to get everything into a Java Type since it is harder to get everything into a KType. First we need a TypeReference type class, we'll steal this from Klutter library as well:
abstract class TypeReference<T> protected constructor() {
val type: Type by lazy {
javaClass.getGenericSuperclass().let { superClass ->
if (superClass is Class<*>) {
throw IllegalArgumentException("Internal error: TypeReference constructed without actual type information")
}
(superClass as ParameterizedType).getActualTypeArguments()[0]
}
}
}
Now a quick extension method to use this:
inline fun <reified T: Any> ft(): Type = object:TypeReference<T>(){}.type
And then our when expression can be more detailed with generics:
for (parameter in function.parameters) {
when (parameter.type.javaType) {
ft<IA>() -> println("Has IA interface parameter.")
ft<ActionEvent>() -> println("Has ActionEvent class parameter.")
ft<A<String>>() -> println("Has A<String> class parameter.") // <---- compilation error in this line
ft<A<Monkey>>() -> println("Has A<Monkey> class parameter.") // <---- compilation error in this line
}
}
But in doing this, we broke inheritance checking again. And we don't really check covariance of the generics (they themselves could have superclass checks).
Double Extra Credit, what about inheritance AND generics?!?
Um, this isn't so much fun. I'll have to think about that one a bit, maybe add it to Klutter later. It is kinda complicated.

Related

Type mismatch using generics

I have issues understanding generics and i failed to find the answer here.
Here is the issue:
I have an abstract class that is suppose to be the parent class to few viewmodels. Idea is, one view is going to be created based on data coming from different viewmodels.
And i want to draw it based on the same method, just using different Types.
Also, I dont want a return type, i want to trigger some callbacks.
Here is the abstract:
package foo.bar.ui.app.user_profile.view_model
abstract class UserDefaultsGenericViewModel : BaseViewModel() {
abstract fun <P> getData(
data: (P) -> Unit,
error: (Result.Error) -> Unit
)
}
And then example of one ViewModel is like this:
package foo.bar.ui.app.user_profile.view_model
#HiltViewModel
class StopViewModel #Inject constructor(
private val getStopsByRouteUseCase: ParamsUseCase<RouteParams, Stops>
) : UserDefaultsGenericViewModel() {
var stopId = ""
override fun <Stops> getData(data: (Stops) -> Unit, error: (Result.Error) -> Unit) {
viewModelScope.launch {
when (val resultStops = getStopsByRouteUseCase.invoke(RouteParams(stopId, Direction.ToWork))) {
is Result.Success -> {
data.invoke(resultStops.value)
}
is Result.Error -> Log.e("TAG", "bar")
}
}
}
}
The problem is in this line:
data.invoke(resultStops.value)
Im getting:
Type mismatch: inferred type is foo.bar.onboarding.Stops but Stops#1 (type parameter of foo.bar.ui.app.user_profile.view_model.StopViewModel.getData) was expected
What am i doing wrong?
You're using a generic method, but it looks like you want a generic class/interface.
In your override fun <Stops> getData, Stops is an arbitrary name of a type parameter, not the actual Stops type that you seem to want. What you probably want instead is the following:
// note the <P> at the class level
abstract class UserDefaultsGenericViewModel<P> : BaseViewModel() {
// no <P> here after the fun keyword
abstract fun getData(
data: (P) -> Unit,
error: (Result.Error) -> Unit
)
}
#HiltViewModel
class StopViewModel #Inject constructor(
private val getStopsByRouteUseCase: ParamsUseCase<RouteParams, Stops>
) : UserDefaultsGenericViewModel<Stops>() { // note the <Stops> type argument here
...
}
Here the <P> is on the class declaration, so that <P> is determined once for each instance of the class. If you declare the generic on the method, the actual type can be different for each method invocation.

Default value for generic member

I'm trying this:
class Foo<T> {
var member: T = T()
}
...but the Kotlin compiler gives me an error: Type parameter T cannot be called as function.
How do I default-construct a generic member variable?
Well, to access the type information, we need to use the reified keyword on the type, but this is only applicable in inlined functions. So instead of relying on direct construction, a workaround can be to use a generator function wrapped in the companion object that immediately sets the member right after construction
// Test class to verify the implementation
class Yolo {
override fun toString() = "Yolo swag"
}
class Foo<T : Any> {
lateinit var member: T
companion object {
inline fun <reified T : Any> newInstance() =
T::class.java.newInstance().let { memberInstance ->
Foo<T>().apply { member = memberInstance}
}
}
}
fun main() {
// generate a Foo<Yolo>
val foo = Foo.newInstance<Yolo>()
println(foo.member) // displays "Yolo swag"
}
It's implied that T has a public no-arg constructor, but in general case it may not be true. This code uses reflection to bypass compiler complains about it (which may end up with runtime error if you dissapoint the JVM expectations and indeed pass T without public no-arg constructor).
//Reified generics at class level are not yet supported in Kotlin (KT-33213),
// so you have to pass instance of `KClass` manually as a consructor parameter
class Foo<T : Any>(clazz: KClass<T>) {
var member: T = clazz.createInstance()
}

Kotlin get runtime class from generic implementation

I'm trying to build a register component that takes in a list of QueryHandlers in my application.
Below you can see the queryHandler and Query definition. They both implement an interface with generics.
class MyQueryHandler(): QueryHandler<MyQuery, MyReturnType>
class MyQuery(val query: String) : Query<MyReturnType>
Now, I have a component in Spring that is using BeanPostProcessor to catch the Bus bean definition and call to it the function registerQueryHandler for each of the query handler he gets in the list.
registerQueryHandler signature is
fun <Q : Query<V>, V> registerQueryHandler(aClass: Class<Q>, queryHandler: QueryHandler<Q, V>)
so it takes the query handler and the class resulting from the query execution.
Below is the pseudocode of my BeanPostProcessor
class SimpleQueryBusPostProcessor(
private val queryHandlers: List<QueryHandler<Q<V>, V>>,
private val beanNameSelector: String
) : BeanPostProcessor {
override fun postProcessAfterInitialization(bean: Any, beanName: String): Any? {
if (beanName == beanNameSelector && bean is SimpleMessageBus) {
queryHandlers.forEach {
bean.registerQueryHandler(#runtimeClassForQ<V>onIt, it)
}
}
return bean
}
}
My problem is on "#runtimeClassForQonIt" I would like to know at runtime, given a specific QueryHandler, which concrete implementation of the Query it is implementing to register it into the Bus.
Any idea on how can I achieve this?
I've tried with some inline function to get it using reified like below, but I only get back the interface that T implements (Query) instead of the concrete class
private inline fun <reified T: Q<P>, P> getClazz(handler: QueryHandler<T, P>) : Class<T> {
return T::class.java
}

How to define functional interface with generics in Kotlin?

I'm learning Kotlin and I have some trouble with functions.
I'm trying to create something like a functional interface with a generic parameter.
In Java I would create something like this:
#FunctionalInterface
public interface Foo<T extends Bar> {
String something(T arg);
}
Then I can use this somewhere else like this (given that Person extends Bar:
Foo<Person> f = p -> p.toString();
How do you write this with Kotlin?
The first thing I tried was to use type-aliases like this:
typealias Foo<T> = (T) -> String
However, it stopped working when I added the bound to the type parameter:
typealias Foo<T: Bar> = (T) -> String // Error: Bounds are not allowed on type alias parameters
The second approach was to write an interface that extends the function type:
interface Foo<T: Bar> : (T) -> String
However, now I don't know how to instantiate a lambda function from with this. It works when I create class from it like this:
class Something: Foo<Person> {
override fun invoke(p: Person): String {
return p.toString()
}
}
val f = Something()
But this is a big overhead and I'm sure there has to be a better solution.
So how can I define a function signature that can be reused by many functions that supports generic parameters with bounds in kotlin?
Most of the time (always?) it is sufficient to define the type of the lambda in the parameter of the function that receives it.
For example:
open class Bar
class Person: Bar()
var f = { p: Person -> p.toString() }
fun <T : Bar> withFoo(block: (T) -> String) { }
fun <T : Bar> otherFoo(block: (T) -> String) { }
fun main() {
withFoo(f)
otherFoo(f)
}
The same way the Kotlin documentation states:
"since Kotlin has proper function types, automatic conversion of functions into implementations of Kotlin interfaces is unnecessary and therefore unsupported."
See https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions

Abstract Class vs. Lambda parameters

Since kotlin has such good support for lambdas, I started to use lambdas as constructor parameters for abstract classes instead of declaring abstract val/fun.
It's more concise in my opinion, especially because val type get's inferred.
What are the downsides to this?
abstract class AbstractListScreen<T> (
val data: Set<T>,
val filterators: (T) -> Set<String>
) {
fun open() {
/* ... */
}
}
class OrderListScreen : AbstractListScreen<Data>(data = setOf(),
filterators = { setOf(it.toString()) }
) {
fun someEvent() {
/* ...*/
}
}
In your example, each instance of OrderListScreen will create its own filterators instance of function type (T) -> Set<String>. This has additional run-time overhead in both memory and performance when compared with abstract functions and their overrides which are stored in the type definition at compile-time.
The default filterators can be stored in a property to reduce this run-time overhead:
class OrderListScreen : AbstractListScreen<Data>(data = setOf(),
filterators = defaultFilterators
) {
companion object {
val defaultFilterators: (Data) -> Set<String> = { setOf(it.toString()) }
}
fun someEvent() {
/* ...*/
}
}
However, each instance of OrderListScreen will still have its own reference to defaultFilterators which is still additional run-time overhead (although marginal unless you have many instances of these types).
Function types such as (T) -> Set<String> may have named parameters (e.g. (element: T) -> Set<String>) but currently IDEs such as IntelliJ IDEA do not use those named parameters in generated documentation or code stubs so such information is lost when subclassing, etc. IDEs do use named parameters in generated documentation and code stubs for abstract functions.
You cannot (currently) associate documentation directly with the function type parameter which you can do with abstract functions.
When attempting to account for the run-time overhead the code doesn't look much different when using abstract functions, the run-time overhead is eliminated, and current IDE support for generated code stubs, documentation, etc. is improved:
abstract class AbstractListScreen<T>(val data: Set<T>) {
abstract fun filterators(element: T): Set<String>
fun open() {
/* ... */
}
}
class OrderListScreen : AbstractListScreen<Data>(data = setOf()) {
override fun filterators(element: Data): Set<String> = setOf(element.toString())
fun someEvent() {
/* ...*/
}
}