I'm getting a little confused here.
So, I have a mutableMap of type
val converters = mutableMapOf<String, Pair<KFunction<ElemBase>, x>>()
where I can't figure out x
I need to add to this map several pairs based on a constructor and a method, such as:
converters["Camera"] = ::Camera to Structure::convertCamera
Where the first field is the reference to a constructor extending ElemBase
, such as:
Camera : ElemBase
and the second is a method on the Structure
class with a reference to a nullable ElemBase
class:
fun convertCamera (dest: KMutableProperty0<Camera?>)
when x
KFunction<*>
works, but I needs something more specific, where Receiver
and argument are defined.
I tried a couple of attempts, such as
KFunction<Structure.(KProperty1<Structure, ElemBase>) -> Unit>
or
KFunction<(KMutableProperty0<ElemBase>) -> Unit>
or
KFunction<Structure.(KMutableProperty0<ElemBase?>) -> Unit>
I always get error whenever I try to add anything to converters
Error:(276, 9) Kotlin: Type inference failed: Cannot infer type parameter V in operator inline fun <K, V> MutableMap<K, V>.set(key: K, value: V): Unit
None of the following substitutions
receiver: MutableMap<String, Pair<Int, KFunction<Structure.(KMutableProperty0<ElemBase?>) -> Unit>>> arguments: (String,Pair<Int, KFunction<Structure.(KMutableProperty0<ElemBase?>) -> Unit>>)
receiver: MutableMap<String, Pair<KFunction0<Camera>, KFunction2<Structure, @ParameterName KMutableProperty0<Camera>, Unit>>> arguments: (String,Pair<KFunction0<Camera>, KFunction2<Structure, @ParameterName KMutableProperty0<Camera>, Unit>>)
can be applied to
receiver: MutableMap<String, Pair<Int, KFunction<Structure.(KMutableProperty0<ElemBase?>) -> Unit>>> arguments: (String,Pair<KFunction0<Camera>, KFunction2<Structure, @ParameterName KMutableProperty0<Camera>, Unit>>)
but no lucks so far
It's interesting to notice that if I assign ::Camera to Structure::convertCamera
to a variable, I get the following type:
Pair<KFunction0<ElemBase>, KFunction2<Structure, @ParameterName KMutableProperty0<ElemBase>, Unit>>
But I do not have KFunction0
neither KFunction2
... what the hell?
Edit: found this
Edit2: if I manually import KFunction0
and KFunction2
it seems it doesnt complain anymore about them. But if I click on them, it cannot resolve..
I tried to import manually both, KFunction0
and KFunction2
, copying the type I saw when I assign the pair to a var
and it looks like this is working:
val converters = mutableMapOf<String, Pair<KFunction0<Camera>, KFunction2<Structure, KMutableProperty0<Camera>, Unit>>>()
But not the corresponding ElemBase
version:
val converters = mutableMapOf<String, Pair<KFunction0<ElemBase>, KFunction2<Structure, KMutableProperty0<ElemBase>, Unit>>>()
Error:(277, 9) Kotlin: Type inference failed: Cannot infer type parameter V in operator inline fun <K, V> MutableMap<K, V>.set(key: K, value: V): Unit
None of the following substitutions
receiver: MutableMap<String, Pair<KFunction0<ElemBase>, KFunction2<Structure, KMutableProperty0<ElemBase>, Unit>>> arguments: (String,Pair<KFunction0<ElemBase>, KFunction2<Structure, KMutableProperty0<ElemBase>, Unit>>)
receiver: MutableMap<String, Pair<KFunction0<Camera>, KFunction2<Structure, @ParameterName KMutableProperty0<Camera>, Unit>>> arguments: (String,Pair<KFunction0<Camera>, KFunction2<Structure, @ParameterName KMutableProperty0<Camera>, Unit>>)
can be applied to
receiver: MutableMap<String, Pair<KFunction0<ElemBase>, KFunction2<Structure, KMutableProperty0<ElemBase>, Unit>>> arguments: (String,Pair<KFunction0<Camera>, KFunction2<Structure, @ParameterName KMutableProperty0<Camera>, Unit>>)
Although I still don't get what's going on with KFunction*
, why they seems to not exist althought once manually imported, the compiler accept them
In case the design smells, I'm porting some c++ code here and I'd like to stick to the original structure if possible
Edit3: ok, I'll probably change design, but I'd like to find out what's going on anyway, for the sake of curiosity
If you write down the following code in IntelliJ:
val map = mutableMapOf("Camera" to (::Camera to Structure::convertCamera))
You can then invoke the "specify type explicitly" intention action on the assignment, or press "Ctrl + Q" on the variable to bring up its type information which is inferred.
This gets you the following:
val map: MutableMap<String, Pair<KFunction0<Camera>, KFunction2<Structure, @ParameterName(name = "dest") KMutableProperty0<Camera?>, Unit>>>
KFunction0
and KFunction2
here are the some classes that Kotlin compiles functions types to (ones that have 0 and 2 parameters, respectively). From the Kotlin in Action book:
The Kotlin standard library defines a series of interfaces, corresponding to different numbers of function arguments:
Function0<R>
(this function takes no arguments),Function1<P1, R>
(this function takes one argument), and so on. Each interface defines a single invoke method, and calling it will execute the function.
You can replace these with the function types instead, and remove some of the unnecessarily specific info, like this:
val map: MutableMap<String, Pair<() -> Camera, (Structure, KMutableProperty0<Camera?>) -> Unit>>
Since you wanted ElemBase
to be in the type instead of the concrete Camera
type, you can do this:
val map3: MutableMap<String, Pair<() -> ElemBase, (Structure, KMutableProperty0<Camera?>) -> Unit>>
However, you can't change KMutableProperty0<Camera>
to KMutableProperty0<ElemBase>
, since this type is invariant, and so this would break the assignment in your example. Just like MutableList<T>
, it both accepts and produces its generic type, therefore it can't accept a subtype or a supertype in its generic parameter instead of the one required.