Is there any practical difference between defining a function inside a companion object, compared to creating it as an extension function on the same companion object?
For example, here:
data class Foo(val s: String) {
companion object {
fun from_as_companion(s: String): Foo {
return Foo(s)
}
}
}
fun Foo.Companion.from_as_extension(s: String): Foo {
return Foo(s)
}
fun main() {
val f1 = Foo.from_as_companion("hello,")
val f2 = Foo.from_as_extension("world")
println("${f1.s} ${f2.s}")
}
From a code-style perspective I think the from_as_extension() approach is slightly more readable, because the reader has fewer levels of nesting to consider; the function body is indented just one level, compared to the 3 levels that function_as_companion is nested to.
There is a byte code difference that I don’t understand – if I look at the generated bytecode in Android Studio the code for both functions is the same except for the entry. from_as_companion looks like:
[...]
public final from_as_companion(Ljava/lang/String;)Lapp/pachli/network/model/Foo;
@Lorg/jetbrains/annotations/NotNull;() // invisible
// annotable parameter count: 1 (visible)
// annotable parameter count: 1 (invisible)
@Lorg/jetbrains/annotations/NotNull;()
L0
ALOAD 1
LDC "s"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkNotNullParameter (Ljava/lang/Object;Ljava/lang/String;)V
[...]
while from_as_extension has an extra checkNotNullParameter:
public final static from_as_extension(Lapp/pachli/network/model/Foo$Companion;Ljava/lang/String;)Lapp/pachli/network/model/Foo;
@Lorg/jetbrains/annotations/NotNull;() // invisible
// annotable parameter count: 2 (visible)
// annotable parameter count: 2 (invisible)
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 1
L0
ALOAD 0
LDC "$this$from_as_extension"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkNotNullParameter (Ljava/lang/Object;Ljava/lang/String;)V
ALOAD 1
LDC "s"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkNotNullParameter (Ljava/lang/Object;Ljava/lang/String;)V
Assuming this isn’t in any performance sensitive code are there any other reasons to avoid extension functions in this case?