Kotlin let,apply,with,run function difference

Kotlin let,apply,with,run function difference

Kotlin has been used for a long time, but only using simple grammar. Recently, sometimes the code is written. The editor automatically prompts the use of let and other functions, and then devotes some time to research.

let

First let() is defined by default that the current object is the it parameter of the closure, and the return value is the last line in the function, or by specifying return.

fun <T, R> T.let(f: (T) -> R): R = f(this)
  • 1

Simple examples:

fun testLet(): Int {
    // fun <T, R> T.let(f: (T) -> R): R { f(this)}
    "testLet".let {
        println(it)
        println(it)
        println(it)
        return 1
    }
}
//Operation result
//testLet
//testLet
//testLet
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

You can see the final generated class file, the code has been formatted, and the compiler just added the contents of the let after our original variables.

public static final int testLet() {
    String str1 = "testLet";
    String it = (String)str1;
    int $i$a$1$let;
    System.out.println(it);
    System.out.println(it);
    System.out.println(it);
    return 1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Let's take a complicated example.

fun testLet(): Int {
    // fun <T, R> T.let(f: (T) -> R): R { f(this)}
    "testLet".let {
        if (Random().nextBoolean()) {
            println(it)
            return 1
        } else {
            println(it)
            return 2
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Compiled class file

public static final int testLet() {
    String str1 = "testLet";
    String it = (String)str1;
    int $i$a$1$let;
    if (new Random().nextBoolean())
    {
        System.out.println(it);
        return 1;
    }
    System.out.println(it);
    return 2;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

apply

Apply function is called an object's apply function. Within the scope of the function, any method of the object can be called arbitrarily and the object can be returned.

fun <T> T.apply(f: T.() -> Unit): T { f(); return this }
  • 1

Code example

fun testApply() {
    // fun <T> T.apply(f: T.() -> Unit): T { f(); return this }
    ArrayList<String>().apply {
        add("testApply")
        add("testApply")
        add("testApply")
        println("this = " + this)
    }.let { println(it) }
}

// Operation result
// this = [testApply, testApply, testApply]
// [testApply, testApply, testApply]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Compiled class file

  public static final void testApply()
  {
    ArrayList localArrayList1 = new ArrayList();
    ArrayList localArrayList2 = (ArrayList)localArrayList1;
    int $i$a$1$apply;
    ArrayList $receiver;
    $receiver.add("testApply");
    $receiver.add("testApply");
    $receiver.add("testApply");
    String str = "this = " + $receiver;
    System.out.println(str);
    localArrayList1 = localArrayList1;
    ArrayList it = (ArrayList)localArrayList1;
    int $i$a$2$let;
    System.out.println(it);
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

with

The with function is a separate function, not an extension in Kotlin, so the call is somewhat different. Return is the last line, and then you can call the object's method directly, feeling like a combination of let and application.

fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()
  • 1

Code example:

fun testWith() {
    // fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()
    with(ArrayList<String>()) {
        add("testWith")
        add("testWith")
        add("testWith")
        println("this = " + this)
    }.let { println(it) }
}
// Operation result
// this = [testWith, testWith, testWith]
// kotlin.Unit
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

class file

 public static final void testWith()
  {
    Object localObject = new ArrayList();
    ArrayList localArrayList1 = (ArrayList)localObject;
    int $i$a$1$with;
    ArrayList $receiver;
    $receiver.add("testWith");
    $receiver.add("testWith");
    $receiver.add("testWith");
    String str = "this = " + $receiver;
    System.out.println(str);
    localObject = Unit.INSTANCE;
    Unit it = (Unit)localObject;
    int $i$a$2$let;
    System.out.println(it);
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

run

The run function is similar to the apply function, except that the run function returns with the last line and the apply returns the current object of its own.

fun <T, R> T.run(f: T.() -> R): R = f()
  • 1

Code example

fun testRun() {
    // fun <T, R> T.run(f: T.() -> R): R = f()
    "testRun".run {
        println("this = " + this)
    }.let { println(it) }
}
// Operation result
// this = testRun
// kotlin.Unit
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

class file

  public static final void testRun()
  {
    Object localObject = "testRun";
    String str1 = (String)localObject;
    int $i$a$1$run;
    String $receiver;
    String str2 = "this = " + $receiver;
    System.out.println(str2);
    localObject = Unit.INSTANCE;
    Unit it = (Unit)localObject;
    int $i$a$2$let;
    System.out.println(it);
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

summary

Well, is it dizzy? That's all right. Let's summarize it.

Function name Definition parameter Return value extension Other
let fun T.let(f: (T) -> R): R = f(this) it Closure return yes  
apply fun T.apply(f: T.() -> Unit): T { f(); return this } No, you can use this this yes  
with fun with(receiver: T, f: T.() -> R): R = receiver.f() No, you can use this Closure return no Calls are different from others
run fun T.run(f: T.() -> R): R = f() No, you can use this Closure return yes  

Posted by poncho4u on Sun, 23 Dec 2018 01:48:06 -0800