科特林OVERRIDE_BY_INLINE

SOF*_*OFe 12 oop inline kotlin

在Kotlin中使用inline功能实现接口时:

interface Foo {
    fun qux(fn: () -> Unit)
}

open class Bar : Foo {
    final override inline fun qux(fn: () -> Unit){TODO()}
}
Run Code Online (Sandbox Code Playgroud)

IDE(可能还有编译器)抱怨以下消息:

Override by an inline function
Run Code Online (Sandbox Code Playgroud)

要取消显示此消息,我必须使用@Suppress("OVERRIDE_BY_INLINE")注释。怎么了?

我已经知道的:

  • 对于普通的内联函数,kotlinc将内联该内联函数的所有用法,但仍会编译该函数的非内联版本,以便可以从Java调用它(并且可能出于向后兼容性或其他原因)。
  • 内联虚拟方法是不可能的。内联只是意味着“将所有代码复制到调用者中”,但是对于调用抽象/接口方法,该实现是在运行时根据所涉及对象的实际类确定的,因此无法知道要复制到哪个实现中功能。

但是,调用最终函数时并非如此。在上面的示例中,当我调用时bar.qux(),编译器可以确保仅使用该特定实现,并且可以安全地内联。它是否覆盖该Foo.qux方法无关紧要-调用foo.qux将使用第1点中提到的非内联版本,并且调用bar.qux可以安全地内联。

只是为了确保开发人员意识到这一警告吗?还是有副作用?

Ada*_*dam 9

我知道这已经很晚了,但这就是原因。你猜对了:

内联虚方法是不可能的。

但是您应该考虑到,虽然Bar.foo它不是虚拟的,因为它可以被覆盖(它不能),但它是虚拟的,因为它可以决定在运行时运行它。考虑以下基于您自己构建的示例:

interface Foo {
    fun qux(fn: () -> Unit)
}

open class Bar : Foo {
    final override inline fun qux(fn: () -> Unit){TODO()}
}

class Baz : Foo {
    override fun qux(fn: () -> Unit) = TODO()
}

fun main() {
    var foo: Foo = Bar()
    foo.qux { }          // calls Bar.qux
    foo = Baz()
    foo.qux { }          // calls Foo.qux
}
Run Code Online (Sandbox Code Playgroud)

这里,Bar.qux最初被调用,但Baz.qux第二次被调用。因此,并非每个调用都可以内联。那么为什么这是一个警告而不是一个编译器错误,就像我们open funinline修饰符声明 an 一样?考虑以下:

val bar: Bar = Bar()
bar.qux { }
Run Code Online (Sandbox Code Playgroud)

在这种情况下,编译器可以安全地内联qux对变量的所有调用,bar因为它被声明为Bar. 即使类在您的示例中是开放的,由于方法本身是final,任何派生类型将始终使用完全相同的qux。所以这个声明,

在上面的例子中,当我调用 bar.qux() 时,编译器可以确保只使用这个特定的实现,并且可以安全地内联。

仅当编译器静态地知道 的类型为bar实际Bar. 这是一个错误,open因为它们都不能被内联,这绝对不是所需的行为,当它被覆盖时是一个警告,因为只有其中一些可以被内联,这可能不是所需的行为。

这个例子可以在 play.kotlinlang.org 上找到