Can anybody explain why generic parameter is lost in Supplier lambda but not in Callable?
I created interface that captures generic type and helper function that instantiates it
abstract class TypeRef<T> protected constructor() {
val type: Type = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
override fun toString(): String {
return type.toString()
}
}
inline fun <reified T> getTypeRef(): TypeRef<T> {
return object: TypeRef<T>() {};
}
But testing it leads to different results depending on where it’s called
inline fun <reified T> testTypeRef(): TypeRef<T> {
val ref0 = getTypeRef<T>()
println("In root $ref0")
Callable {
val ref1 = getTypeRef<T>()
println("In callable $ref1")
}.call()
Supplier {
val ref2 = getTypeRef<T>()
println("In supplier $ref2")
""
}.get()
return ref0
}
produces
In root java.util.List<? extends java.util.concurrent.atomic.AtomicReference<java.lang.String>>
In callable java.util.List<? extends java.util.concurrent.atomic.AtomicReference<java.lang.String>>
In supplier T
I’m just guessing here but it might have something to do with the generic definition of Callable and Supplier.
They are defined like this in the java stdlib.
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
@FunctionalInterface
public interface Supplier<T> {
T get();
}
My guess is that T in the last case no longer referes to your reified T but instead refers to the T of the Supplier interface. My test shows that if you rename your type parameter to V you get this output
In root java.util.List<? extends java.lang.String>
In callable V
In supplier java.util.List<? extends java.lang.String>
So that seems to agree with my guess.
Not sure if this is a bug or an unintended side effect of how generics work.
Wow, so actual name of generic parameter plays role here. It’s even more confusing than I thought. I renamed T to Z and all three prints gave correct generic parameter. It would be nice if Kotlin compiler can take care of it.
Yeah, this feels like there should at least be a warning here. I only noticed this because I looked up the declarations of Supplier and Callable. If I hadn’t seen that Callable uses a different name for the type parameter I don’t think I would have found this.