In the last article, we have seen several classes unique to the Kotlin language — data classes, closed classes, etc., and we are familiar with the common operators of Kotlin sets, so we don’t have to worry about Kotlin sets in the future. This is the fourth chapter in the notes series.

1. Kotlin scoped functions

If you’ve used Kotlin in your projects, you’ve probably seen the let function! Because every time Kotlin detects that an object might be empty, it automatically helps us to change it to the let function: user.name?.let{textView.text = it}. The let function here is the scope function of Kotlin. In addition to let, there are run, with, apply, also, and so on.

Scope functions are functions built into Kotlin that perform a series of operations and transformations on data. Similar to set operators, scope functions can be called not only by set objects, but also by all objects. Let’s see how they’re used.

Let and run are similar in that they both return the result of the closure inside the function, except that let takes a closure argument and run does not. Use: let{closure}, run{closure}, with the closure argument meaning that let can get itself in the closure through it; Run, on the other hand, doesn’t work. You can only get it through the this keyword. Look at the code 1 example.

// code 1 data class Car( val brand: String, val price: Int ) var car: Car? = null fun funcExample() {car = car (" red flag ", 199999); val carBrand = car? = null fun funcExample() {car = car (" red flag ", 199999) .let {"car's brand is ${it.brand}"} println(carBrand) // let {"car's brand is ${it.brand}"} println(carBrand) // .run { "car's price is ${this.price}" } println(carPrice) }Copy the code

The Also and apply functions do not return the results of closures, whereas the let and run functions above do. Also, the difference between the also and apply functions is whether they have closure arguments: Also has a closure argument, and apply does not. But they all return caller objects, so they support chained calls.

// Code 2 // Also in the closure you can use it to access the caller, followed by a chingled call to car? . Also {println(" brand = ${it.brand}")}? .brand = "byd" // the apply closure uses this to access the caller. .apply {println(" brand = ${this.brand}")}? .apply { println("car's price = ${this.price}") }Copy the code

The takeIf and takeUnless scope functions are used less often. Closures in the takeIf function return Boolean type, or the caller if the closure condition is met, or null if it is not. Take chestnuts as an example.

// code 3 car? .takeIf { it.price > 1500000 } ? .also {println(" Cars are too expensive!" } // If the closure is true, it is not null, then execute the also closure? : run {println(" the price is ok! ") } // If the closure is false, return null and execute the run closureCopy the code

TakeUnless is the opposite of takeIf. TakeUnless returns null if its closure condition is met, and returns the caller if it is not.

Repeat function. Call method: repeat(times) {closure}. Execute the closure operation times. The IT inside the closure is the number of loops currently executed, counting from 0.

// code 4 repeat(3) { println("car' brand is ${car? .brand}, price is ${car? $it")} Result: car' brand is BYD, price is 199999 current index: 0 car' brand is BYD, price is 199999 current index: 1 Car 'brand is BYD, price is 199999Copy the code

With the function. Call method: with(T){closure}. In Android development, when you need to assign a value to a TextView, you can use with.

// code 5 with(textView) {text = "test" textSize = 20F setTextColor(contextCompat.getColor (context, contextCompat.getColor); R.color.purple_200)) }Copy the code

2. Kotlin custom operators

After learning Kotlin for a while, you’ll see that Kotlin gives developers a lot of room for self-development. For example: support new extension functions to the class, support operator overload and so on. Therefore, we can also customize some operators to facilitate development. Those of you who have looked at Kotlin’s built-in implementation of operators will notice that these functions are inline. Let’s start with the inline keyword.

The inline keyword, which can be seen as a marker for inline or not. At compile time, the body of the function is “copied”, which means that the code of the inline function is placed directly in the location of the called function. This is different from normal functions, where the instructions jump to the entry address of the called function. After the function is executed, Instruction to jump back to the original jump to continue to execute the following code; Since an inline function puts the code of the function directly in the position of the function, there is no instruction jump, and the instructions are executed in order. Doing so speeds up the code, but increases compilation time and the amount of compiled code.

The inline keyword is suitable for less complex functions that are frequently called. So Kotlin’s operators are inline, and if we want to define our own operators, we need to make them inline. Here is a custom convert operator that acts like the map function in the collection.

// code 6
inline fun <T, E> Iterable<T>.convert(action: (T) -> E): Iterable<E> {
    val list: MutableList<E> = mutableListOf()
    for (item in this) list.add(action(item))
    return list
}
Copy the code

3. The use of backquotes in Kotlin

In the previous Kotlin Study Notes (1), Kotlin backquotes were introduced to deal with the problem of Kotlin keyword conflicts in Java code. Backquotes also serve the purpose of turning an invalid character into a valid character in Kotlin code. Here’s an example:

// code 7 object SmallTips {fun '123' (){println(" function name is' 123 '! "); } fun ' '(){println(" function name is''! ") } fun ' '(){println(" function name is''! ") '123' () smalltips. ' '() smalltips.' '() smalltips.' '() smalltips.' '() smalltips.' '() smalltips.' '() SmallTips.Copy the code

Incredible! Function names cannot be pure numbers or Spaces, but backquotes will do! Magic! So what’s the use? Remember Kotlin’s internal access modifier? It restricts the function it decorates to be used only in the current module and not in other modules. But Java does not have this modifier, and Kotlin and Java must be fully compatible, so Java has to support this feature.

Here’s the problem: decompile Kotlin’s internal modified functions to public modified functions (Cry of joy.gif) in the generated Java code. To prevent Java from accessing functions in Kotlin, you can change the names of these functions in Kotlin to illegal, and then enclose them in backquotes. By doing so, Java code cannot call these methods, whereas Kotlin can call, This gives you the effect of masking some Kotlin functions in Java. Finally, this use of backquotes is not recommended! Understand can!

4. Kotlin object comparison

In Java, to compare two objects for equality, you usually use the == or equals method. The == operator in Java compares the values of the two objects themselves, that is, the first addresses of the two objects in memory. In the case of two strings, it compares whether the two strings store the same address.

Object in Java, it is the first address in memory to store the starting address, behind its address is used for storing the addresses of it contains each attribute, so memory will use multiple blocks of memory to store objects of each attribute value, and through the first address. Then you will find the object, which can find the object’s attributes.

The Equals method in Java compares the values of attributes in two objects. In the case of two strings, whether the contents of the two strings being compared are the same.

In Kotlin, == and === are used to determine whether two objects are equal. That’s right, two equals and three equals. Kotlin == is equivalent to Java’s equals method; And === is equivalent to the Java == operator, just remember that. Chestnuts are also available, see below:

// code 8 val str1 = java.lang.String(" I send ") val str2 = java.lang.String(" I send ") println("str1 == str2" Println ("str1 == str2 = ${str1 == str2}") println("str1 == str2 = ${str1 == str2}"Copy the code

Since Kotlin’s String constructor doesn’t pass a String directly, the Java String class is used for initialization. 1) val str1 = StringBuilder(” I send “).toString(); 2) val str1 = String(” toByteArray() “).

5. Kotlin’s constant variable

According to note 1, Kotlin has two types of variables. One is an immutable variable modified by the val keyword. The other is a mutable variable decorated with the var keyword. How do we initialize these two variables in our class? Val is immutable, so it can only override getters, var can override getters and setters, and of course the class automatically generates them for us.

// code 9 class Person {var age: Get () {return field.plus(10)} set(value) {field = value -1} val name: Get () {return field + "haha"} val height: Get () {return (age * 2F + 10)}} Float () {return (age * 2F + 10)}}Copy the code

When you override the getter and setter methods, you can get the value of the property through the field. The essential difference between val and var is that there is no setter method for val. Val is not a constant, but we cannot assign to val again! Why is val not a constant? If you look at the height variable in Code 9, when the age changes, the height changes, it’s not a constant.

To declare a constant, use the const keyword. It has two cavs: 1) Const can only be used to modify object attributes, or top-level variables. 2) The value of a const variable must be determined at compile time, so the type must be String or a primitive data type.

What does that mean? What I understand is that Kotlin’s const modifier is similar to an immutable static variable in Java. It declares only three things:

  1. Inside the object class, object decorates static classes;
  2. Top-level position, i.e. outside of a class;
  3. Inside the Companion Object, where static variables are declared.
// code 10 object ValAndVarExample {const val t2 = "heiheihei"} const val t1 = "hahaha" // top level, Class Person {companion object{const val t3 = "hehehe"}}Copy the code

6. Special use of Kotlin inline, crossinline, and noinline keywords

The basic use of the inline keyword was explained earlier in Section 2, Kotlin’s Custom operators, and the feature that inline functions can speed up program execution by copying code directly to the place of call. In addition to the inline keyword, there are crossinline and noinline keywords to see what other special uses they can have.

But before we do that, we need to understand some of the premises. The inline keyword affects both the function object itself and the passed Lambda closure argument, both of which are inlined to the call point.

The compiler preprocessor will extend the inline function, eliminating the need for parameter pressing, assembly language CALL generation, return parameters, return and other processes, so as to improve the running speed. The advantage is that after the function is inlined, the compiler can perform more in-depth optimization of the resulting code through context-specific optimization techniques; But it makes the compiled code larger, but saves the overhead of function calls. So inline is appropriate for simpler, frequently invoked functions.

6.1. A Lambda expression in a function that is inline to interrupt calls to an external function.

What you mean? It’s okay. We’re all confused. Let’s take an example and say:

// code 11 fun main(args: Array<String>) {test1 {println(" I want to learn Kotlin") return} println(" I want to learn Android")} inline fun test1(lambda: () -> Unit) {lambda.invoke()} //Copy the code

Test1 is inline, and it has a Lambda closure with a return function in it that interrupts the outer main function, so it just prints “I’m going to learn Kotlin.”

In Kotlin, an internal Lambda closure cannot interrupt the execution of an external function. Try to remove the inline modifier test1 from code 11. The compiler will prompt that return must be written as return@test1. Return test1, not main.

6.2. The crossinline keyword does not allow Lambda expressions in inline modified functions to interrupt the execution of external functions.

In code 11, what if the return of a Lambda expression only wants to interrupt the execution of the closure, but not the execution of the outer main function? Someone might say, “Why don’t I use inline?” But inline? You can use crossinline to decorate the Lambda closure, and the compiler will not inline the Lambda expression.

// code 12 fun main(args: Array<String>) {test1 {println(" I want to learn Kotlin") return@test1 println(" I want to learn ~")} println(" I want to learn Android")} inline Fun test1(crossinline lambda: () -> Unit) {lambda.invoke()} // OutputCopy the code
6.3. The noinline keyword disallows Lambda expressions in inline modified functions to be inlined.

First, the noinline keyword is applied to the Lambda closure; Second, it is used to remove the effect of the inline keyword on a Lambda closure in a function decorated with the inline keyword, leaving it as a normal Lambda closure. Description is not enough, code to gather!

// code 13
inline fun test2(lambda0: () -> Unit, noinline lambda1: () -> Unit): () -> Unit {
    lambda0.invoke()
    lambda1.invoke()
    return lambda1
}
Copy the code

The test2 function is inline, takes two Lambda closures as arguments, and its return value is also a Lambda closure. If lambda1 does not have the noinline keyword, it will copy the function body directly to where it was called, just like lambda0. In this case, lambda1 will not return as a closure, so code 13 will report an error after dropping noinline. So if you want test2 to be inline and you want to return a closure, you can use the noinline keyword to remove the effect of inline on the closure.

All of the above is an advanced use of the inline keyword, which is not normally used as a reserve of knowledge.

This note ends, more Kotlin study notes can be seen: Kotlin study notes (1) Kotlin study notes (2) Kotlin study notes (3) – Kotlin dynamic Agent Can you write?

Not enough? Welcome to my public number around ~

reference

  1. Zhang; Geek time Kotlin series
  2. The little white sheep in the mountains; Blog.csdn.ne/QQ_33757398…
  3. WeiBang bar; Equals in the Java and the use of the = = (introduces) www.cnblogs.com/weibanggang…
  4. One_Month; Noinline in Kotlin blog.csdn.net/One_Month/a…
  5. DONGYUXIA; Kotlin Basic inline functions blog.chinaunix.net/uid-3147827…

If you give a rose, you leave a fragrance in your hand. Welcome to retweet, share and follow, your recognition is the spiritual source of my continued creation.