Kotlin let,apply,run,with and other functions distinguish 2

Keywords: network

Kotlin let,apply,run,with and other functions distinguish 2

I've also summarized the differences between Kotlin's built-in functions let, apply, run and with before. address After that, we add other, takeIf, takeUnless and other functions, so we recapitulate here and introduce the use scenarios.

Premise introduction

Like Groovy and other languages, Kotlin supports closures. If the last parameter in a function is a closure, the last parameter can be written after the parentheses instead of in parentheses. If there is only one parameter, the parentheses can be removed.

As shown below

fun toast() {
    button.setOnClickListener({
       Toast.makeText(context, "test", Toast.LENGTH_SHORT).show()
    })
}

fun toast() {
    button.setOnClickListener {
        Toast.makeText(context, "test", Toast.LENGTH_SHORT).show()
    }
}

The following functions are all like this, which is easy to understand.

repeat

The repeat function is a separate function defined as follows.

/**
 * Executes the given function [action] specified number of [times].
 *
 * A zero-based index of current iteration is passed as a parameter to [action].
 */
@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
    contract { callsInPlace(action) }

    for (index in 0..times - 1) {
        action(index)
    }
}

It's easy to understand by code how many times a block is executed in a loop.

as

fun main(args: Array<String>) {
    repeat(3) {
        println("Hello world")
    }
}

Operation result

Hello world
Hello world
Hello world

with

The with function is also a separate function, not an extension in Kotlin. The specified T acts as the receiver of the closure, using the return result of the closure in the parameter.

/**
 * Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

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

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);
  }

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)

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

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;
}

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
        }
    }
}

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;
}

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.

/**
 * Calls the specified function [block] with `this` value as its receiver and returns `this` value.
 */
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

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]

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);
  }

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.

/**
 * Calls the specified function [block] with `this` value as its receiver and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
fun <T, R> T.run(f: T.() -> R): R = f()

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

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);
  }

Another Run

There's also a run function, not extension, which is defined as follows: execute the block, return the block

/**
 * Calls the specified function [block] and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

Example

fun main(args: Array<String>) {
    val date = run {
        Date()
    }

    println("date = $date")
}
// Operation result
// date = Thu Jan 04 19:31:09 CST 2018

also

Execute the block and return to this.

/**
 * Calls the specified function [block] with `this` value as its argument and returns `this` value.
 */
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

Example:

fun main(args: Array<String>) {
    val also = Date().also {
        println("in also time = " + it.time)
    }

    println("also = $also")
}

Operation result

in also time = 1515065830740
also = Thu Jan 04 19:37:10 CST 2018

takeIf

If the condition in block is satisfied, the current value is returned, otherwise the return value of null and block is Boolean type.

/**
 * Returns `this` value if it satisfies the given [predicate] or `null`, if it doesn't.
 */
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (predicate(this)) this else null
}

Example

fun main(args: Array<String>) {
    val date = Date().takeIf {
        // Is it after New Year's Day 2018?
        it.after(Date(2018 - 1900, 0, 1))
    }

    println("date = $date")
}

// Operation result
// date = Thu Jan 04 19:42:09 CST 2018

takeUnless

Contrary to takeIf, if the condition in block is not satisfied, the current object is returned, otherwise null

/**
 * Returns `this` value if it _does not_ satisfy the given [predicate] or `null`, if it does.
 */
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (!predicate(this)) this else null
}

Example

fun main(args: Array<String>) {
    val date = Date().takeUnless {
        // Is it after New Year's Day 2018?
        it.after(Date(2018 - 1900, 0, 1))
    }

    println("date = $date")
}
// Operation result
// date = null

summary

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

Function name Definition block parameter Closure returns the return value Function return value extension Other
repeat fun repeat(times: Int, action: (Int) -> Unit) nothing Unit Unit no Ordinary function
with fun with(receiver: T, f: T.() -> R): R = receiver.f() No, you can use this Any Closure return no Ordinary function
run run(block: () -> R): R nothing Any Closure return no Ordinary function
let fun T.let(f: (T) -> R): R it Any Closure return yes
apply fun T.apply(f: T.() -> Unit): T No, you can use this Unit this yes
run fun T.run(f: T.() -> R): R No, you can use this Any Closure return yes
also fun T.also(block: (T) -> Unit): T it Unit Closure return yes
takeIF fun T.takeIf(predicate: (T) -> Boolean): T? it Boolean this or null yes Closure return type must be Boolean
takeUnless fun T.takeUnless(predicate: (T) -> Boolean): T? it Boolean this or null yes Closure return type must be Boolean

Example

The above is what I understand, and finally give an overall example.

Define a structure

class User {
    var id: Int = 0
    var name: String? = null
    var hobbies: List<String>? = null

    override fun toString(): String {
        return "User(id=$id, name=$name, hobbies=$hobbies)"
    }
}

Ordinary assignment statements so you can

var user = User()
user.id = 1
user.name = "test1"
user.hobbies = listOf("aa", "bb", "cc")
println("user = $user")

If you use let,apply,run,with, let and also need it, the other defaults use this.

user.let {
    it.id = 2
    it.name = "test2"
    it.hobbies = listOf("aa", "bb", "cc")
}
println("user = $user")

user.also {
    it.id = 3
    it.name = "test3"
    it.hobbies = listOf("aa", "bb", "cc")
}
println("user = $user")

user.apply {
    id = 2
    name = "test2"
    hobbies = listOf("aa", "bb", "cc")
    Date()
}
println("user = $user")

user.run {
    id = 3
    name = "test3"
    hobbies = listOf("aa", "bb", "cc")
    Date()
}
println("user = $user")

with(user) {
    id = 4
    name = "test4"
    hobbies = listOf("aa", "bb", "cc")
    Date()
}
println("user = $user")

Another example is the response structure of http.

class Resp<T> {
    var code: Int = 0
    var body: T? = null
    var errorMessage: String? = null

    fun isSuccess(): Boolean = code == 200

    override fun toString(): String {
        return "Resp(code=$code, body=$body, errorMessage=$errorMessage)"
    }
}

When dealing with network data, we need various judgments, such as.

fun main(args: Array<String>) {
    var resp: Resp<String>? = Resp()

    if (resp != null) {
        if (resp.isSuccess()) {
            // do success
            println(resp.body)
        } else {
            // do fail 
            println(resp.errorMessage)
        }
    }
}

Of course, you can also use apply,let,run and other functions.

fun main(args: Array<String>) {
    var resp: Resp<String>? = Resp()

//    if (resp != null) {
//        if (resp.isSuccess()) {
//            // do success
//            println(resp.body)
//        } else {
//            println(resp.errorMessage)
//        }
//    }

    resp?.run {
        if (isSuccess()) {
            // do success
            println(resp.body)
        } else {
            println(resp.errorMessage)
        }
    }

    resp?.apply {
        if (isSuccess()) {
            // do success
            println(resp.body)
        } else {
            println(resp.errorMessage)
        }
    }

    resp?.let {
        if (it.isSuccess()) {
            // do success
            println(it.body)
        } else {
            println(it.errorMessage)
        }
    }

    resp?.also {
        if (it.isSuccess()) {
            // do success
            println(it.body)
        } else {
            println(it.errorMessage)
        }
    }
}

Posted by jonez on Sat, 22 Dec 2018 06:18:06 -0800