Marti Serra Molina
Marti Serra Molina

Reputation: 473

Why String.() -> Unit is the same as (String) -> Unit in my code?

I am struggling to understand how a function type with receiver works in Kotlin. I don't understand why we can use (String) -> Unit and String.() -> Unit in my code indistinctively

fun main() {
    var showStringFunction1: (String) -> Unit = ::showString
    showStringFunction1("Hey")
    var showStringFunction2: String.() -> Unit = ::showString
    showStringFunction2("Hey")
}

fun showString(x: String): Unit { println(x) }

Upvotes: 2

Views: 1322

Answers (3)

Animesh Sahu
Animesh Sahu

Reputation: 8096

Both the (String) -> Unit and String.() -> Unit are mapped to same JVM signature i.e. implementation of interface KFunciton1<String, Unit>. That's why they behave the same.

The only difference you'll find is that when you are creating these you'll be presented with this in case of String.() -> Unit while any variable as of your choice in (String) -> Unit.

While in case of referencing it is the same. However in Kotlin-1.4 I heard they are separating them (don't know if its true, will update answer if I confirm it).

Upvotes: 1

Alexey Soshin
Alexey Soshin

Reputation: 17691

Trying to reason about it the other way around:
String.() -> Unit would be translated by Kotlin compiler to (String) -> Unit, hence they are the same.

Using "Show Kotlin Bytecode"/"Decompile" you'll get the following result from both cases:

public final class AKt/BKt {
   private static final void showString(String x) {
      boolean var1 = false;
      System.out.println(x);
   }

   public static final void main() {
      Function1 showStringFunction2 = (Function1)null.INSTANCE;
      showStringFunction2.invoke("Hey");
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}

Upvotes: 1

Tenfour04
Tenfour04

Reputation: 93551

The difference between these only matters when either:

  1. You are passing a lambda. It affects whether the first parameter is this or a named parameter inside the lambda. If you pass something other than a lambda, the receiver can be thought of as the first function argument.

  2. You are invoking it. The receiver version is more versatile because you can also choose to invoke it like an extension function. But you can also invoke it as if the receiver is the first function argument.

A related concept comes when passing a function argument. So, showString could be an extension fun String.showString(): Unit and it could be passed to either of your variables in the example.

Upvotes: 3

Related Questions