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?

+3


source to share


1 answer


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>>)
}

      

+1


source







All Articles