The scope function in Kotlin is enough

Posted by BenS on Wed, 19 Jan 2022 01:50:47 +0100

This article mainly shares the usage and differences of Kotlin's scope functions let, run, with, apply and also.

During the execution of scope function, a temporary scope is formed through lambda expression, which can reduce the number of some logic codes.

Context object: this or it

this

run, with, and apply reference context objects with the keyword this. Therefore, context objects can be accessed in their lambda expressions as in normal class functions.

it

let and also take context objects as lambda expression parameters. If no parameter name is specified, the object can be accessed with the implicit default name it. It is shorter than this, and expressions with it are usually easier to read.

Return value

  • apply and also return context objects.
  • let, run and with return the result of lambda expression

let function

let function is often used in daily development. It is generally used for non empty judgment for subsequent operations, The context object {it} represents the return value of lambda as a parameter expressed by lambda.

data class User(var name: String)
fun main() {
    val user = User("Wangwei")
    //Both let and run will return the execution result of closure. The difference is that let has closure parameters, while run has no closure parameters
    val letResult: String = user.let { user: User -> "let::${user.javaClass}" }
    // When there is only one parameter in the let, the closure parameter can be omitted and replaced by it
    val letResults: String = user.let { "let::${it.javaClass}" }
    println(letResult)
    
    var str :String? = "hello"
    // If the value of str is null, the function in the let will not execute
    str?.let {
        println(it.length)
    }
    
    // For chained call results
    val numbers = mutableListOf<String>("one","two","three","four","five")
    numbers.map { it.length }.filter { it > 3 }.let {
        println(it)
    }
}

with function

It is a non extended function that calls multiple methods or attributes of the same object. It eliminates the duplication of object names and directly calls method names and attributes. The context object is this

val numbers = mutableListOf<String>("one","two","three","four","five")   

with(numbers) {
    println("with argument is $this")
    println("it contain $size element")
    //println("it contain ${this.size} element") has the same effect as the above line
}

run function

The context object is this, which returns a lambda object. It combines the functions of let and with to call multiple methods and attributes of the same object, eliminating repeated calls of object names, directly calling attribute and method names, and uniformly performing null judgment.

numbers.run {
        println("with argument is $this")
        println("it contain $size element")
    }

    var str1 :String? = "hello"
    str1?.run { 
        println("str1 length is $length")
        println("char is ${get(1)}")
    }

apply function

The context object is this, and the return value is the object itself, which can be called continuously on the object. It is mainly used for attribute assignment during object initialization.

data class Person(var name: String, var age: Int = 0, var city: String = ""){}
var testUser = Person("Xiao Ming").apply {
        age = 18
        city = "Shanghai"
    }
    println(testUser)

also function

The context object is used as a lambda expression parameter {it}, which is similar to the let parameter

val numbers = mutableListOf<String>("one","two","three","four","five")

numbers.also {
    println("with argument is $it")
    println("it contain ${it.size} element")
}.add("ha-ha")

How to select a scope function

  • Execute a lambda expression on a non null object: let
  • Introducing an expression as a variable into a local scope: let
  • Object configuration: apply
  • Object configuration and calculation result: run
  • Run the statement where an expression is needed: non extended run
  • Additional effect: also
  • A set of function calls for an object: with

takeIf and taceUnless

For object detection and filtering, it is often used in combination with the above scope functions in development.

//The closure of takeIf returns a judgment result. When it is false, the takeIf function returns null
//takeUnless is just the opposite of takeIf. When the judgment result of closure is true, the function will return null
user.takeIf { it.name.length > 0 }?.also { println("Name is ${it.name}") } ?: println("Name is empty")
user.takeUnless { it.name.length > 0 }?.also { println("Name is empty") } ?: println("Name is ${user.name}")

Today is the weekend. Have a nice weekend, readers.

I think the article is good. Give me some praise. Pay attention!
Technology exchange can be concerned about the official account [Junwei], plus my friends to explore together.

Topics: Android Design Pattern Flutter kotlin