OVERRIDE_BY_INLINE in Kotlin
Asked Answered
S

1

24

When implementing an interface in Kotlin with an inline function:

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

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

the IDE (and possibly the compiler) complains with this message:

Override by an inline function

To suppress this message, I have to use the @Suppress("OVERRIDE_BY_INLINE") annotation. What is wrong?

What I am already aware:

  • For a normal inline function, kotlinc will inline all usages of the inline function, but it will still compile the non-inline version of the function so that it can be called from Java (and possibly for backward compatibility or whatever reason).
  • It is impossible to inline a virtual method. Inline simply means "copy all the code into the caller", but for invoking an abstract/interface method, the implementation is determined at runtime based on the actual class of the object involved, so it is not possible to know which implementation to copy into the function.

However, this isn't the case when invoking a final function. In the example above, when I invoke bar.qux(), the compiler can ensure that only this particular implementation will be used, and can safely inlined. It is irrelevant with whether it overrides the Foo.qux method -- calling foo.qux will use the non-inline version mentioned in point 1, and calling bar.qux can inline safely.

Is this warning there only to make sure developers are aware of this? Or are there side effects?

Sweepstakes answered 7/11, 2018 at 11:26 Comment(8)
do you not also get "redundant modality modifier" for final?Exclave
No. override is open by default, and you need final for inline.Sweepstakes
One thing that you definitely won't be able to do in an inlined override is to call the super method.Putsch
Oh I forgot to put the open modifier on Bar. It was open in my own code.Sweepstakes
You can do things in inline methods that are impossible in non-inlined methods. Not all implementations of Bar.qux can be made to work for foo.qux.Cower
What exactly are these things?Sweepstakes
@Sweepstakes what did you do in the end?Karnes
Suppress the warning, since nobody told me anything wrong with itSweepstakes
N
14

I know this is pretty late, but here's why. You got this right:

It is impossible to inline a virtual method.

But you should consider that, while Bar.foo is not virtual in the sense that it could be overridden (it can't be), it is virtual in the sense that a decision can be made to run it at runtime. Consider the following example which builds on your own:

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
}

Here, Bar.qux is invoked initially, but Baz.qux is invoked the second time. So, not every call can be inlined. So why is this a warning and not a compiler error, like when we declare an open fun with the inline modifier? Consider the following:

val bar: Bar = Bar()
bar.qux { }

In this case, the compiler can safely inline all calls to qux on the variable bar because it's declared as a Bar. Even if the class is open as in your example, since the method itself is final, any derived type will always use exactly that qux. So this statement,

In the example above, when I invoke bar.qux(), the compiler can ensure that only this particular implementation will be used, and can safely inlined.

is true only when the compiler statically knows the type of bar to be an actual Bar. It's an error when it's open because none of them can be inlined which definitely isn't the desired behavior, it's a warning when it's an override because only some of them can be inlined, which might not be the desired behavior.

This example is available at play.kotlinlang.org

Neighborly answered 17/1, 2020 at 18:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.