除非扩展类,否则Kotlin内联方法不可见

时间:2016-09-19 01:28:48

标签: extension-methods kotlin

我在库中遇到了一个问题,我写的是零垃圾收集。我写了一个myFunction函数,但是我有一个问题,我无法调用该函数,除非我扩展该类(在这种情况下)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)
    }       
}

这是错误:

Error:(23, 8) Kotlin: Unresolved reference: myFunction

2 个答案:

答案 0 :(得分:3)

问题是您在RandomClass的不同接收者中为RandomClass的某个实例编写了扩展函数。因此,它只能与RandomClass一起使用,其中this的实例RandomClass可以与显式或隐含的接收者一起推断。 Kotlin无法同时指定类的实例和不同的接收器。您只能在指定一个时执行此操作,而另一个则可以隐含。

如果我们嘲笑它,问题可能更明显:

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

如何同时指定AB? &#34;实例A接收器B没有语法调用foo()&#34;。

但如果您已经在A内,例如:

class A {
   inline fun B.foo() { ... }

   fun bar() { B().foo() }  <-- all good!  We have A, we have B, and can call foo
}   

A的实例由类本身满足,接收者在调用B之前创建Foo的新实例。与您的代码唯一不同的是您调用了A实例和B接收器相同的东西,但它们是需要知道的两个参数才能进行此类函数调用。

在您的情况下,您有两个简单的选项可以摆脱对实例和接收器的需求:

<强> 1。不要将myFunction作为扩展功能,只能将其设为内联:

open class RandomClass<out E>() {
    fun iterator(): Iterator<E> {
        TODO()
    }

    inline fun myFunction(action: (E) -> Unit): Unit {
        for (e in iterator()) action(e)
    }
}

<强> 2。将内联扩展移到类外,这样它就不需要实例:

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)
}

无论哪种方式,您都不再有编译错误。

答案 1 :(得分:0)

class A {
   inline fun B.foo() { ... }
}   

foo被称为成员扩展函数,因为它是类A的成员和类B的扩展。 foo内部有两个接收器:

  • this@A称为调度接收器,
  • this@foo或简称this称为扩展接收方。
  

如何同时指定A和B? &#34;实例A接收器B没有语法调用foo()&#34;。

实际上有这样的语法,你只需要A作为隐式this调度接收器:

with(A()) {
   B().foo()
}

这里有A实例指定为隐式调度接收器,B实例作为显式扩展接收器。

对于来自问题的类,它会是什么样子:

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 {  }
}

但在你的情况下,没有必要进行myFunction成员扩展,因为它内部没有使用两个接收器。只需将其设为成员或扩展名,而不是两者,如this answer所示。