@AutoReducer & generics in Kotlin
jhavatar opened this issue · 6 comments
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>,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?
@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.
@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.
cool, thanks Yarikx.
I'll do a hack for now. Please reply on this thread when a version is released with updated kotlin support.
Opened discussion thread for this issue here: https://discuss.kotlinlang.org/t/declaration-site-variance-and-java-interop/2331
@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:
- Get rid of wildcard in Reducer type parameter
@AutoReducer
abstract class KotlinReducer : Reducer<List<@JvmSuppressWildcards TodoItem>> {
...
}
That will make Kotlin represent the class as Reducer<List<TodoItem>>
and not as Reducer<List<? extends TodoItem>>
- Leave Reducer state as is, but annotate state argument in each action handler with
@JvmWildcard
fun addItem(state: List<@JvmWildcard TodoItem>, item: TodoItem): List<TodoItem> {
return state.plus(item)
}
Hope that helps.
Yes, solves the problem. Thanks, will come in handy.