I stuck on one issue with Kotlin Generics
I have one data processor implementations, which works with generic Operation and ResultItem class as you can see on the image. Then I have method that provide specific operation implementation based on item that I need to process. But for some reason Kotlin convert <out Number> generic to Nothing type, so Float as input for processing is not accepted. Do you know why?
There is a clue in your own post, somewhere in: “Kotlin convert <out Number> generic to Nothing type, so Float as input for processing is not accepted”. out means you can only output numbers, but you try to input them.
There are several problems with your code:
1 . As above: using out in Operation<out T> makes this class useless. It is used for consuming T, not for providing T.
2 . You can’t return Operation<Number> from getOperation(). That would mean the returned object can process any number, but you return a very specific FloatOperation that works only with floats, so you break type safety here. You have to make getOperation() generic:
fun <T : Number> getOperation(type: Class<T>): Operation<T> {
return FloatOperation() as Operation<T>
}
Unchecked cast should be safe as long as you verified T is Float.
3 . Then you have another problem with input::class.java returning Class<out Float> instead of Class<Float>. I’m too lazy to explain what is the problem here, but if you know the type of input then request the class directly: Float::class.java. If you only know input is some Number then I believe you need another generic function with unchecked cast:
fun <T : Number> process(input: T) {
getOperation(input::class.java as Class<T>).process(ResultItem(input))
}
Thanks. Basically, I want to break a type safety. I want to make the processing part. It means that I have a queue of many ResultItem that holds same data and I have sets of Operation implementation. And I just want to iterate over all items, choose right Operation implementation, process item, ResultItem object that will hold some statistical data about processing.
But I still want to have generics on all object, because it helps a lot with implementation of Operation that works only with one data type. It also helps a lot with API for adding new items to queue, but processing itself will always works with all data types and I need to have there just top parent class. It is reason why I have Number there as common parent. I simply want to tell compiler that they can be all existing children of Number.
class ResultItem<out T : Number>(val result: T)
abstract class Operation<in T : Number> {
abstract fun process(item: ResultItem<T>)
}
class FloatOperation : Operation<Float>() {
override fun process(item: ResultItem<Float>) {
TODO("Not yet implemented")
}
}
fun <T : Number> getOperation(type: Class<T>): Operation<T> {
return FloatOperation() as Operation<T>
}
fun main() {
val input = 10.1f
val a = getOperation(Float::class.java)
a.process(ResultItem(input))
}
Notable changes:
ResultItem<out T : Number> because it only has a getter, so it can only output values of type T.
Operation<in T : Number because Operation only consumes values of type T.
Cast because of what @broot is explained. In real code, of course, you should actually check that type is actually Float, which is presumably why you pass type in in the first place.
Float::class.java instead of input::class.java because polymorphism. Well, of course Float is not really polymorphic, but the language rules are the same for every type. Let’s imagine that input has the compile time type Animal, but its runtime type is Cat. Then input::class.java would be Class<out Animal> meaning that objects of this class are suitable for output as objects of type Animal. However, you’re passing it to getOperation() asking bascially for an operation that can input objects of type T, which is not known exactly. So as far as the compiler is concerned, getOperation may return at run time an operation that can only consume cats (err, that’s gross) while what you meant to ask for an operation that can consume all kinds of animals.