Reputation: 2188
I've ran into an issue in a library I'm writing for zero-garbage collections. I've wrote a myFunction
function but I have an issue where I can NOT call the function unless I extend the class (in this case) RandomClass
package com.charlatano
fun main(args: Array<String>) {
val hello = RandomClass<String>()
hello.myFunction { // Unresolved reference: myFunction
}
}
class myClass {
private val list = RandomClass<String>()
fun loop() {
list.myFunction { // Unresolved reference: myFunction
}
}
}
class myClassInherit : RandomClass<String>() {
private val list = RandomClass<String>()
fun loop() {
list.myFunction { // Compiles without issue
}
}
}
open class RandomClass<out E>() {
fun iterator(): Iterator<E> {
TODO()
}
inline fun <E> RandomClass<E>.myFunction(action: (E) -> Unit): Unit {
for (e in iterator()) action(e)
}
}
Here is the error:
Error:(23, 8) Kotlin: Unresolved reference: myFunction
Upvotes: 2
Views: 976
Reputation: 23125
class A {
inline fun B.foo() { ... }
}
foo
is called member extension function, because it is a member of class A
and an extension for class B
. Inside of foo
there are two receivers available:
this@A
is called dispatch receiver,this@foo
or simply this
is called extension receiver.How can you specify both A and B at the same time? There is no syntax for "Instance A receiver B call foo()".
Actually there is such syntax, you just need to have A
as implicit this
dispatch receiver:
with(A()) {
B().foo()
}
Here you have instance of A
specified as implicit dispatch receiver and instance of B
as explicit extension receiver.
How it would look like with classes from question:
val randomClass = RandomClass<Any>()
val anotherRandomClass = RandomClass<Any>()
with(randomClass) {
// randomClass is both dispatch receiver and extension receiver
myFunction { }
// randomClass is dispatch receiver and anotherRandomClass is extension receiver
anotherRandomClass.myFunction { }
}
But in your case there is no need to make myFunction
member extension, because it doesn't use two receivers inside. Just make it either member or extension, not both, as this answer suggests.
Upvotes: 0
Reputation: 85946
The issue is that you wrote an extension function for some instance of RandomClass
within a different receiver of RandomClass
. So it can only be used from with RandomClass
where both the instance this
of RandomClass
can be inferred along with the explicit or implied receiver. There is no way in Kotlin to specify both an instance of a class and a different receiver at the same time. You can only do it when you specify one and the other can be implied.
The problem maybe is more obvious if we mock it up:
class A {
inline fun B.foo() { ... }
}
A().foo() <--- nope, need B().foo() within A()
B().foo() <--- nope, can't see B.foo() from outside A instance (scoped extension)
A::B.foo() <--- doesn't exist as syntax, I made this up
How can you specify both A
and B
at the same time? There is no syntax for "Instance A receiver B call foo()".
But if you are inside of A
already, for example:
class A {
inline fun B.foo() { ... }
fun bar() { B().foo() } <-- all good! We have A, we have B, and can call foo
}
The instance for A
is satisfied by the class itself, and the receiver by the new instance of B
being created before Foo
is called. The only different from your code is that you called A
instance and B
receiver the same thing but they are two parameters that need to be known to make this type of function call.
In your case you have two simple options to get rid of the need for both instance and receiver:
1. Don't make myFunction
an extension function, only make it inline:
open class RandomClass<out E>() {
fun iterator(): Iterator<E> {
TODO()
}
inline fun myFunction(action: (E) -> Unit): Unit {
for (e in iterator()) action(e)
}
}
2. move the inline extension outside of the class so it doesn't also need an instance:
open class RandomClass<out E>() {
fun iterator(): Iterator<E> {
TODO()
}
}
inline fun <E> RandomClass<E>.myFunction(action: (E) -> Unit): Unit {
for (e in iterator()) action(e)
}
Either way, you have no compiler errors anymore.
Upvotes: 3