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