preface

Hello, everyone. Long time no see. It’s been nearly a decade since Kotlin was released. More and more developers are using Kotlin, especially Android developers, since Google officially announced Kotlin as a tier 1 developer language for Android at I/O 2017. Kotlin was announced as the first android development language at I/O 2019.

I started to learn Kotlin in the second half of 2018. At first, I bought a copy of “Kotlin from Zero to Master”, followed it and learned the basic grammar of Kotlin. Since the company was relatively small, there were not many Android developers, and there was a new project at that time. Therefore, I proposed to adopt Kotlin for the development of the whole project. Actually, it was quite risky at that time, because the project was in a rush, and the three Android developers were not good at Kotin, so I kept learning and writing during the development of the project. After the completion of the project, I felt that I had almost understood Kotlin (in fact, I only knew how to use it. Some parts of the project still use Java).

Later, I saw that more and more people were learning Kotlin in some developer forums, but I couldn’t understand a lot of things mentioned. The most excessive thing was that I had already written a project using Kotlin, and I didn’t know what the key words in Kotlin were for. I was really ashamed.

Before has been concerned about guo God’s blog, and android entry also see guo God’s “second line of code”. Later, I heard that the third Line of Code would be published, and it had Kotlin’s detailed explanation, so I immediately grabbed a signed copy of the book when it was released on the first day. The company let me go on a business trip before I came and had a good look at it. I haven’t had time to look at it until now. I didn’t use a lot of Kotlin’s good stuff. The Kotlin I used just turned Java code into Kotlin, which didn’t make any sense. Okay, here we go. Knock on the blackboard and underline!

Kotlin’s standard function

Standard functions in Kotlin refer to functions defined in standard.kt. Here are some Standard functions that I think are often used.

let

One of the main reasons we decided to use Kotlin in the first place was that it brought null-pointer exceptions to the language level, but it’s also a source of frustration for many developers like me.

Yes, Kotlin is not allowed to define it as empty for space safety, so you have to put a question mark on it, but… Many cases look like the following code:

For Kotlin’s space safety it is necessary to use question marks or two exclamation points to eliminate space safety errors. Just to eliminate space safety errors, it is not necessary in Java. It’s fine if we only use it once or twice, we use question marks or exclamation marks, but what about a bunch of calls? For example:

Parameter when how to do. Not to mention the speed of writing, bored to death…

And then… There was no need to write that! It could go like this:

    var zhu:ZhuJ? = null

    fun test(a){ zhu? .let { zhu-> zhu.name zhu.phone zhu.age zhu.sex } }Copy the code

Does the code suddenly feel much more elegant… The global variables are fine, and the method arguments are even better.

Guys, if I knew, I wouldn’t have to write a bunch of question marks and exclamation marks…

with

What this standard function does is that the code in the Lambda holds the context of the object, the last line of which is the return value.

It’s a little hard to understand, but here’s the code:

operator fun String.times(n: Int): String {
    val sb = StringBuilder()
    repeat(n){
        sb.append(this)}return sb.toString()
}
Copy the code

This is an operator overload of the String class, which simply means repeating the String several times. As you can see, the StringBuilder object in this method is used several times, and often needs to be written once, but… If you use the with standard function…

operator fun String.times(n: Int): String {
    return with(StringBuilder()) {
        repeat(n) {
            append(this@times)
        }
        toString()
    }
}
Copy the code

Do you feel a little fresh, a lot of situations can be called like this.

run

This standard function is basically the same as with, except that the method is different. With requires an object to be written in parentheses to operate, while run is an object point to operate.

operator fun String.times(n: Int): String {
    return StringBuilder().run {
        repeat(n) {
            append(this@times)
        }
        toString()
    }
}
Copy the code

apply

The last line of apply is not used as a return value. The last line of apply is not used as a return value.

operator fun String.times(n: Int): String {
    return StringBuilder().apply {
        repeat(n) {
            append(this@times)
        }
    }.toString()
}
Copy the code

The last line of both with and run returns a value, but apply does not.

The keyword

This piece needs a good summary of the next, really is, have written a project even the use of language keywords did not recognize the whole. One at a time!

lateinit

The lateinit keyword is used a lot. It is not necessary to set the question mark to nullable when defining a global variable as null. If you are sure it is not null, you can use lateInit, for example:

lateinit var zhuJ: ZhuJ
Copy the code

When you define a global variable like this you don’t have to set it to nullable, like in the Android project where we can be sure that the adapter is assigned and not empty, so we can use LateInit.

This should be noted that, even if we think it will not be empty, there will certainly be special cases that need to be judged. In this case, isInitialized should be used, and the method of use is as follows:

if (::zhuJ.isInitialized){
    // Determine whether an assignment has been made
}
Copy the code

sealed

This keyword has not been used before, it is used to modify the class, meaning for the sealing class, before I have not understood the sealing class what to say, these two days have a good look, I understand the function is: can make the code more strict.

We usually define an interface, and then define a successful class and a failed class to implement the interface, and then judge:

class Success(val msg: String) : Result
class Fail(val error: Throwable) : Result

fun getResult(result: Result) = when (result) {
    is Success -> result.msg
    is Fail -> result.error.message
    else -> throw IllegalArgumentException()
}
Copy the code

The above code is what we usually write, although there are only two cases, but you have to write else to check, if you don’t write otherwise the compilation will not pass. This is not the case with sealed classes:

sealed class Results
class Success(val mag: String) : Results()
class Failure(val error: Exception) : Results()

fun getMessage(result: Results) {
    when (result) {
        is Success -> {
            println(result.mag)
        }
        is Failure -> {
            println(result.error.toString())
        }
    }
}
Copy the code

Not only do you no longer have to write else, but kotlin checks to see if the condition includes all subclasses when making a when judgment. If not, kotlin will prompt you to add it, which greatly improves the robustness of the code and prevents undetected problems.

operator

This keyword is operator overloading, which is already used in the standard function above, where you can recustomize the operator to fulfill requirements that don’t work in the code, but actually work, and it’s also comfortable to use.

Here’s a look at the overloaded functions of our common operators: plus corresponds to plus, minus corresponds to minus, times corresponds to times, divisor corresponds to div, mod corresponds to REM, increment corresponds to inc, and decrement corresponds to dec.

Here’s how to use it:

operator fun String.times(n: Int): String {}
Copy the code

internal

This keyword can be used to modify classes and methods. It simply restricts access to different modules. If an internal method is defined in A Module, it can only be called in A Module, but not in B Module.

inner

This keyword is simple enough to modify a class, but… Can only be used to modify inner classes.

Let’s write a chestnut for you to know:

class Test {

    var num: String? = null
    
    class Zhu() {
        var nums: String? = null
        fun adds(a){ nums? .let { it.length } } } innerclass Jiang() {
        var nums: String? = null
        fun adds(a){ nums? .let { it.length } } } }Copy the code

The above code is very simple. It just defines a Test class, one of which is the Zhu class created directly internally, and the other is the Jiang class decorated with the inner keyword. Let’s just call it and see what the difference is:

    Test().Jiang().nums
    Test.Zhu().nums
Copy the code

If you want to use inner to modify the inner class, you need to obtain the instance of the Test class before you can use it, while the Zhu class directly created does not need to be used.

inline

This keyword stands for inline function and is easy to use by adding the inline keyword to a higher-order function. If you don’t know much about higher-order functions, I suggest you watch a video on throwline, which seems to be about Lanbda.

In a nutshell, higher-order functions are not as difficult as they seem. The name just sounds fancy. In a nutshell, passing in a method (which is essentially an object) as a method argument is a higher-order function. The principle of higher-order functions is to convert method arguments to interfaces and create anonymous inner classes to call, so each call to such a Lambda creates a new instance of the anonymous inner class and interface, causing additional overhead.

So that’s where inline comes in, it gets rid of that overhead, there’s nothing special about it, it just does the substitution, you just replace the method arguments where you call them to reduce the memory and performance overhead. Here’s how to use it:

inline fun high(block:(Int.Int) -> Int,block2:(Int.Int) - >Int){
    block.invoke(5.6)
    block2.invoke(4.5)}Copy the code

noinline

This keyword and the above inlined function keyword seems to be it! This is because if there are two or more method arguments in a higher-order function, the inline keyword will change all method arguments to inline functions. Why not replace them all? Since the function type arguments of an inline function are replaced at compile time, there are no real parameter types, but the function type arguments of a non-inline function can be passed freely to any other function, whereas the function type arguments of an inline function are only allowed to be passed to another inline function.

In order to explain why noinline exists, we can use it simply:

inline fun high(block:(Int.Int) -> Int.noinline block2:(Int.Int) - >Int){
    block.invoke(5.6)
    block2.invoke(4.5)}Copy the code

It is as simple as adding the noinline keyword before the method argument.

crossinline

Now that we’ve said noinline, we should also say crossnoinline. It lets methods that cannot use inline functions use inline functions.

Why are there functions that can’t use inline functions? Non-inline functions cannot return directly, but inline functions can, so an error is reported if another implementation of a Lambda or anonymous class is created or used in a higher-order function. Here’s another chestnut:

Runnable: Runnable: Runnable: Runnable: Runnable: Runnable: Runnable: Runnable That’s where the crossinline keyword comes in. It allows functions that can’t use inline functions to use inline functions:

Perfect solution! Crossinline is used to ensure that the return keyword is never used in Lambda expressions in inline functions, so there are no conflicts. The downside of this is that we can’t call a return from Runnable.

infix

This keyword is actually very useful, we can use it to do some very naughty operations:

 val result = "zhujiang" * 3
 val a = result begin "zhu"
Copy the code

Have you never seen this written before? Copying it to your computer will definitely give you an error. Infix’s main function is to define semantically comfortable ways to write things like result begin “zhu” above:

infix fun String.begin(prefix:String):Boolean = startsWith(prefix)
Copy the code

Is not very easy to use, is not already thought of a lot of SAO operation? Ha, ha, ha

But!

Note the following two points:

  1. Infix cannot be defined as a top-level function. It must be a member function of a class and can be defined into a class using extension methods
  2. The infix function must accept only one argument, and there is no limit to the type.

by

This keyword means delegate. Here’s how to use it:

class MySet<T>(val help:HashSet<T>) :Set<T> by help{

    override fun isEmpty(a): Boolean {
        return false}}Copy the code

You can create delegate classes for some classes and override them or add your own methods.

The generic

Generics are familiar, and we use them a lot in Java, such as List, HashMap<String,String>, and so on.

Generics in Kotlin

In fact, use the same as in Java, chestnut again:

class Generic <T>{

    fun method(parem:T):T{
        return parem
    }
    
}
Copy the code

The above is used in class, of course can also be used in method:

    fun <S> meth(parem:S):S{
        return parem
    }
Copy the code

The implementation of generics

Absolutely not in Java, and not practical, because Java’s generics erase mechanism…

But it’s possible in Kotlin, but… Conditional!

  1. Functions must be inline because only inline functions have substitution operations.
  2. A type must be declared with the reified keyword to indicate that the generic is to be implemented.

So, what does that do? Take a look at the code:

inline fun <reified T> startActivity(context:Context) {
    context.startActivity(Intent(context,T::class.java))
}
Copy the code

Got it. Very convenient!

Contravariant and covariant generics

This… The next article will be devoted to contravariant and covariant generics. Owe first!

conclusion

In fact, there are many interesting things in Kotlin that we need to explore, such as coroutines. In fact, we used a lot of coroutines in the project, but we always feel that they are not used well enough, so we need to buckle them in time. Although the content is not much, but also wrote for a long time, the knowledge of the article is to follow Guo God’s “third line of code” to learn, also recommend you to buy, a book can learn a knowledge point is not loss, support legal, don’t download piracy to save dozens of dollars…

If this article has helped you, don’t forget the triple link. If there is anything inappropriate or wrong in the description of this article, please mention it, thank you very much. That’s it. I’ll see you later.