Let's say I have the following code:
open class Fruit
class Apple : Fruit()
open class Juice<T : Fruit>
class AppleJuice : Juice<Apple>()
fun <F : Fruit, J : Juice<F>> makeJuice(juiceClass : Class<J>, fruit : F) : J {}
I call the function like this:
val appleJuice : AppleJuice = makeJuice(AppleJuice::class.java, Apple())
But instead of passing a class object I would like to pass AppleJuice as a type:
val appleJuice : AppleJuice = makeJuice<AppleJuice>(Apple())
I have refactored my function to inline with reified:
inline fun <F : Fruit, reified J : Juice<F>> makeJuice(fruit : F) : J {}
But now I have to specify both types:
val appleJuice : AppleJuice = makeJuice<Apple, AppleJuice>(Apple())
In theory, Apple type shouldn't be needed because it's already known from AppleJuice type. Is it somehow possible to get rid of passing unnecessary types and pass only those maked as reified?
The main problem I see with your solution is, that you are asking for 2 generic types on your
makeJuice-method. BothFandJneed to be given to the function. While it is obvious for you (and anyone looking at the method), I think it might not be that obvious during runtime when the generic types are erased (but that is now mainly a guess here).If you do not mind that the fruits you are passing are not really matching subtypes of the juice you are expecting then the following may be something for you:
If however you want to ensure that
AppleJuicecan only be constructed withApples, then I can only think of solutions similar to the following:adding the
makeJuiceto theFruitclasses, e.g.adding a
makeJuice(/makeFrom?) to theJuiceclasses, e.g.adding any other intermediate object so that you do not require 2 generic types at once, e.g.
and calling it with
variants of the above using extension functions, e.g.
but you need to specify it for all types. The following unfortunately will not work:
as we then have the same problem again... and need to specify
<Apple, AppleJuice>.But maybe none of these are what you were hoping to get. So if you want to have a single method that handles it all, the third variant is probably your best choice (even though it uses a wrapper).