https://kotlinlang.org logo
Title
m

Marko Mitic

10/12/2019, 8:02 PM
I'm having an issue with Kotlin generics and generic java classes. I'm trying to write a function for a DI lib that will takes two Class instances, one of interface and one of class implementing the interface (akin to Dagger's
@Binds
). So far, I can make it work with Kotlin types and even Java ones but it fails for generic Java types (like List<*> and ArrayList<*>). Is this limitation of generics system or am I doing something wrong? function:
fun <C : I, I: Any> bind(clazz: Class<C>, iface: Class<I>)
usage:
bind(ArrayList::class.java, List::class.java) //kotlin types, works fine
bind(java.util.ArrayList::class.java, java.util.List::class.java) // error: inferred type ArrayList<*> is not a subtype of List<*>
m

marstran

10/12/2019, 8:04 PM
What error do you get?
m

Marko Mitic

10/12/2019, 8:05 PM
added it to "usage" part
Full error
Type parameter bound for C in fun <C : I, I : Any> bind(clazz: Class<C>, iface: Class<I>): Unit
 is not satisfied: inferred type ArrayList<*> is not a subtype of List<*>
m

marstran

10/12/2019, 8:11 PM
Right. My guess is that it’s because the type parameter on the Java types are invariant.
While the Kotlin ones are covariant.
m

Marko Mitic

10/12/2019, 8:15 PM
Any idea for a workaround?
Interestingly enough, calling the function from java works fine 😄
m

marstran

10/12/2019, 8:25 PM
So the workaround is to use Kotlin types in Kotlin and Java types in Java? 😛
m

Marko Mitic

10/12/2019, 8:26 PM
looks so 😄
m

molikuner

10/12/2019, 8:32 PM
Just overload the function:
inline fun <C : I, I: Any> bind(clazz: KClass<C>, iface: KClass<I>) = bind(clazz.java, iface.java)
If you need to do this often, you may want to publish an extra artifact with those overloaded functions.
m

Marko Mitic

10/12/2019, 8:40 PM
That gives the same error unfortunately
Type parameter bound for C in inline fun <C : I, I : Any> bind2(clazz: KClass<C>, iface: KClass<I>): Unit
 is not satisfied: inferred type ArrayList<*> is not a subtype of List<*>
Called like this
bind2(java.util.ArrayList::class, java.util.List::class)
t

Timmy

10/13/2019, 11:05 PM
For a similar problem the following solution was suggested to me: Instead of having one function, split it into two parts like so:
fun <I: Any> bind(iface: Class<I>) = BindOp(iface)
class BindOp<T>(val t: T)
infix fun <I:Any, C:I> BindOp<I>.to(clazz: Class<C>): Unit = TODO()