Confusion with Kotlin generics
I am new to Kotlin and I am trying to write code that does something pretty simple, however I cannot figure out how to use generics to get it to work.
I have a trait Handler
that is a handler for things. I cannot change the code for the handler as it comes from the library .
trait Handler<T> {
fun handle(result: T)
}
All the codes below are under my control -
User
- Open a class that has subclasses such as AdminUser
and GuestUser
etc.
The AdminUserAction
trait that creates the AdminUsers list then passes the list to the handler for List<AdminUser>
-
trait AdminUserAction {
fun then(handler: Handler<List<AdminUser>>)
}
Now I want to pass a AdminUserAction
handler for User
instead of AdminUser
. Let's say the handler just registers the usernames and does nothing with the specific admin properties.
fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) {
action.then(printAllNames)
}
However, this code gives me a TypeMismatch.
Since the handler is of type List<T>
and immutable, the previous code should be completely safe, however the compiler cannot understand it.
If I had access to the code for the Handler, I could do the following and it would work -
trait Handler<in T> {
fun handle(result: T)
}
However, as I said before, I cannot modify the Handler as it comes from the library. Also, it seems like a hack to do this because the handler type is completely generic and should be used for other kinds of handlers as well.
I tried the subclass handler and used it -
trait ListHandler<in T>: Handler<List<T>> { }
However, now I am getting an error: "Parameter T is declared as" in ", but occurs at an" invariant "position in Handler>"
I tried -
trait ListHandler<in T>: Handler<List<in T>> { }
But this is giving me more errors.
Why is this so confusing? And how can I use generics to make the previous code work?
Edit:
I can get it to work by writing a generic function that converts a Handler<List<User>>
to Handler<List<AdminUser>>
-
fun <T: User> fromGeneric(handler: Handler<User>): Handler<T> {
return object: Handler<T> {
override fun handle(result: List<T>) {
handler.handle(result)
}
}
}
And then -
fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) {
action.then(fromGeneric(printAllNames))
}
But it seems so wasteful. Look especially at the body of the transform function fromGeneric
. He does nothing ! However, I have to go through rigamarole using it every time to satisfy the types.
Is there a better way? Is it technically possible to make the Kotlin compiler smarter so that this type of focus isn't needed?
source to share
There are several solutions:
Change the definition AdminUserAction
to
trait AdminUserAction {
fun then(handler: Handler<in List<AdminUser>>)
}
or change the definition AdminUserAction
to
trait AdminUserAction {
fun then(handler: Handler<List<User>>)
}
or just enter printAllNames
like this
fun doIt(action: AdminUserAction, printAllNames: Handler<List<User>>) {
action.then(printAllNames as Handler<List<AdminUser>>)
}
source to share