preface

This blog is my study notes, if there is something wrong, please point out in the comment section, please forgive me

Previous link

If the last argument in the function is a closure, then the last argument can be written after the parentheses instead of inside the parentheses. If there is only one argument, the parentheses can also be removed. (This is an important concept that will be used to understand the source code.)

1. The with and run functions

We hope that the comparison of the two functions can deepen our learning impression

@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T. () - >R): R = receiver.block()
Copy the code

The with function takes an object of type T and a function that is treated as an extension function. This method basically tells the T object to execute the body function. Since the second argument is a function, the second function can be placed outside parentheses. We can create a block of code in the second argument that uses this and directly accesses public methods and properties to return the last line of results.

@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T. () - >R): R = block()
Copy the code

The run function is also an extension function. Similar to the apply function, except that the run function uses the last line of return, and the apply function returns its current object, this. (We’ll talk about the apply function at the end)

We compare this in both Java and Kotlin

// java

TextView text = findViewById(R.id.tv_text)
text.("ymc")
text.setTextSize(23)
Copy the code

With that in Java code, let’s use kotlin code to write out how the with and run functions work

// kotlin

/ / with function
with(R.id.tv_text) {
    tv_text.text = "ymc"
    tv_text.textSize = 23f
}

/ / run function
R.id.tv_text.run {
    text = "ymc"
    textSize = 23f
}
Copy the code

kotlin code

protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      this.setContentView(2131296283);
      int var2 = -1100088;
      ((TextView)this._$_findCachedViewById(id.tv_text)).setText((CharSequence)"ymc");
      ((TextView)this._$_findCachedViewById(id.tv_text)).setTextSize(23.0 F);
      var2 = -1100088;
      ((TextView)this._$_findCachedViewById(id.tv_text)).setText((CharSequence)"ymc");
      ((TextView)this._$_findCachedViewById(id.tv_text)).setTextSize(23.0 F);
   }
Copy the code

The above two pieces of code implement the same function, and the kotlin code after conversion is the same, compared to Java, Kotlin code is more concise. The argument in the with/run function is an object, and we can use methods that refer directly to the object’s public properties or methods without using the method name.

Different:

  1. The with function is an ordinary function, and the run function is an extension function
  2. Simplicity of the function when it might be empty
  3. Since the run function is a combination of the let and with functions, it makes up for the fact that the let function must use it arguments instead of objects in the body of the function. In the run function, it can be omitted like the with function, and directly access the public properties and methods of the instance. On the other hand, it makes up for the null judgment problem of the object passed in to the with function. The run function can do the same thing as the let function. It can be used in any scenario of the let,with function.

Let’s take an example where WebSetting may be empty and see how these two functions behave:

/ / with function

with(webview.settings) {
      this? .javaScriptEnabled =true
      this? .databaseEnabled =true}}/ / run functionwebview.settings? .run { javaScriptEnabled =true
    databaseEnabled = true
}

Copy the code

In this case, the run extension function is obviously better because we can check nullability before using it. If you need to use an object multiple times in a piece of code, you can use the with/run function.

2. Let function and run function

@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) - >R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)}Copy the code

The let function is an extension function that defaults to the current object as the it argument to the closure and returns the last line in the function, or specifies return

If we compare t. ray and t. let, the two functions are very similar, the only difference is that they take different parameters. The same logic for both functions is shown below.

/ / run functionstring? .run { println("The length of this String is $length")}/ / functionstring? .let { println("The length of this String is ${it.length}")}Copy the code

kotlin code

      String string = "ymc";
      String var4 = "The length of this String is " + string.length();
      System.out.println(var4);
      var4 = "The length of this String is " + string.length();
      System.out.println(var4);
Copy the code

If you look at the t.run function declaration, you’ll notice that t.run is only treated as a block of calls to the block: t. () extension function. Therefore, in its scope, T can be referred to by this. In most cases this can be omitted during coding. So in our example above, we can use length instead of length instead of length in println statements instead of {this.lenght}. So I’m going to call this passing this argument

The declaration of T et, you’ll notice that T et is passed itself to the function block: (T). So this is similar to passing a lambda expression as a parameter. It can be referred to within the scope of a function using it. So I’m going to call this passing it parameters

3. Let function and Also function

fun <T> T.also(block: (T) - >Unit): T
Copy the code

The also function is an extension function that defaults to the current object as the it parameter of the closure, returning the passed parameter itself.

Let’s compare the differences and similarities between let and also.

/ / functionstring? .let { println("The length of this String is ${it.length}")}/ / braking functionstring? .also { println("The length of this String is ${it.length}")}Copy the code

However, they differ subtly in that their return value let returns a different type of value, while also returns the T type itself, which is this.

Both functions are useful for chaining calls to functions, where let lets you evolve operations and ALSO lets you perform operations on the same variables.

There’s a way we can understand it better

// Normal use
fun makeDir(path: String): File  {
    val result = File(path)
    result.mkdirs()
    return result
}
// Combine
fun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }

Copy the code

From the code above we can see that the let function passes String, but returns a File object (by default, returns the result of the last line of the expression). Also returns File, which is it itself

3. Apply the function

fun <T> T.apply(f: T. () - >Unit): T
Copy the code

The apply function is also an extension function. Within the scope of the function, you can call any method on the object and return the object

Let’s look at an example of how apply works

/ / normal
fun createIntent(intentData: String, intentAction: String): Intent {
    val intent = Intent()
    intent.action = intentAction
    intent.data=Uri.parse(intentData)
    return intent
}
/ / the apply function
fun createIntent(intentData: String, intentAction: String) =
        Intent().apply { action = intentAction }
                .apply { data = Uri.parse(intentData) }

Copy the code

Multiple library functions in the selection of the standard (original source has been confused who is the actual)