Yarikx / reductor

Redux for Android. Predictable state container library for Java/Android
Apache License 2.0
463 stars 27 forks source link

@AutoReducer & generics in Kotlin #20

Closed jhavatar closed 7 years ago

jhavatar commented 7 years ago

When using Kotlin, My simple todo list reducer fails to build/generate its implementation with the following error:

error: First parameter arg0 of method addItem(java.util.List,io.chthonic.template.kotlin.data.model.TodoItem) should have the same type as state (java.util.List<? extends io.chthonic.template.kotlin.data.model.TodoItem>) 1 error

The reducer:

    @AutoReducer.Action(
            value = TodoListActions.ADD_ITEM,
            from = TodoListActions::class)
    fun addItem(state: List<TodoItem>, item: TodoItem): List<TodoItem>  {
        return state.plus(item)
    }

The Action:

    @ActionCreator.Action(ADD_ITEM)
    fun addItem(item: TodoItem): Action

Note, that the following horrible version of the reducer does build in Kotlin:

    @AutoReducer.Action(
            value = TodoListActions.ADD_ITEM,
            from = TodoListActions::class)
    fun addItem(state: List<Any>, item: TodoItem): List<TodoItem>  {
        return (state as List<TodoItem>).plus(item)
    }

Since "List<? extends X>" parameter does not have an equivalent in Kotlin, as far as I know, do you know a better solution to get my reducer to build?

Yarikx commented 7 years ago

@jhavatar Thank you for this issue. To be honest, I didn't spend much effort on Kotlin support :), but that was my next big step.

I need to investigate why Kotlin represents type differently from Java while compilation.

One ad-hoc solution that comes to mind: you can still declare your args as List<? extends X>:

    @AutoReducer.Action(
            value = TodoListActions.ADD_ITEM,
            from = TodoListActions::class)
    fun <T> addItem(state: T, item: TodoItem): List<TodoItem>  where T : List<TodoItem> {
        return state.plus(item)
    }

As that is a dirty and ugly solution, this should work. I will look into it and add first class support for Kotlin types soon.

Yarikx commented 7 years ago

@jhavatar No, sorry, the last example doesn't work either with the production version of Reductor, sorry. Another workaround is to wrap List into some other class which doesn't have generics.

jhavatar commented 7 years ago

cool, thanks Yarikx. I'll do a hack for now. Please reply on this thread when a version is released with updated kotlin support.

Yarikx commented 7 years ago

Opened discussion thread for this issue here: https://discuss.kotlinlang.org/t/declaration-site-variance-and-java-interop/2331

Yarikx commented 7 years ago

@jhavatar Ok, I found a solution! http://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#variant-generics

So as the issue is caused by kotlin-specific declaration-site variance and Java interop, it cannot be really solved from the reductor side. Instead, you can tweak type declarations to help compiler a bit. The options are:

    fun addItem(state: List<@JvmWildcard TodoItem>, item: TodoItem): List<TodoItem>  {
        return state.plus(item)
    }

Hope that helps.

jhavatar commented 7 years ago

Yes, solves the problem. Thanks, will come in handy.