preface

Because of the demands of work, a lot of time has recently been spent promoting Kotlin. So most recent articles have been about Kotlin.

This is not a detailed article, but a quick way to make the transition from Java (Android) to Kotlin, so it’s more of a mix of syntax and ideas.

Several features for quick start Kotlin

Fully understand Kotlin, fast writing business

Quickly switch to Kotlin for Android mode

Let’s talk about coroutines in Kotlin. Nice

The body of the

No more bullshit. I’m sure everyone here is here to learn technology. Let’s do it.

A, attributes,

Everyone, do not because of the title of the attribute, feel no nutrition, think I want to say what var, val. No, no, no, no, no, no. There’s a lot more to kotlin’s attributes.

1.1 Observable property Observable

This language feature is very, very useful and practical. Don’t believe it? See below for a demo:

class User {
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")}}fun main(a) {
    val user = User()
    user.name = "first"
    user.name = "second"
}
Copy the code

Whether the effect is good or not, let’s look at the “curative effect”, and the operation results are as follows:

Is that interesting? Any changes to our name property are observed. We can “muck around” in the callback function. Whatever. Some of you might want to say, well, since I’m listening for attribute changes, why don’t I secretly change some attributes? Of course we can, but we need the following function.

1.2, vetoable

Vetoable is a hook function that accepts an expression for evaluation. Those that satisfy this expression are assigned a value, otherwise discarded. Very simple, go to demo:

// Filter sets that do not meet the criteria
var max: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
    newValue > oldValue
}

println(max) / / 0

max = 10
println(max) / / 10

max = 5
println(max) / / 10
Copy the code

2.1. The delay attribute Lazy

Lazy () is a function that takes a lambda and returns an instance of lazy, which can be used as a delegate to implement the delay property: The first call to get() executes the lambda expression passed to lazy() and records the result, and subsequent calls to get() simply return the result of the record.

Lazy loading, to put it bluntly. A variable identified by lazy triggers the expression we implemented when called,

(We’ll focus on expressions later, but we only need the last line of the expression, which represents a return.)

And get the return value of the expression. But the expression is executed only once, and subsequent calls directly return the expression’s return value. That is what we often call lazy loading.

val lazyValue: String by lazy {
    println("computed!")
    "Hello"
}

fun main(a) {
    println(lazyValue)
    println(lazyValue)
}
Copy the code

And if you look at the result, it’s pretty clear.

entrust

All of these things are collectively referred to in Kotlin as the delegate/delegate property. Delegates are a useful language feature that can even help us solve complex design pattern problems. This is interesting, but also hope friends to explore their own yo ~


Ii. Expressions

For my learning, the lack of understanding of expressions caused great confusion for me to read the code at first. The main reason is that the return of “dawei” is missing, which makes me confused. So here, let’s talk about expressions that also fill in the hole.

1.1. If expressions

The if keyword, which stands for an expression in Kotlin, will have a return by default, the last line in the expression. Such as:

var num = if(... Some judgment){Awesome!
}else{
    66666
}
Copy the code

Here the value of num is 666. If has its own return function. Is it impossible to use the ternary operator? Do need the ternary operator (condition? Then: otherwise) is written differently (but not inoperable) in Kotlin, because plain if will do the job.

// as an expression
val max = if (a > b) a else b

val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}
Copy the code

? In Kotlin, : means: If the value on the left is empty, the value on the right is taken.

More interesting symbol usage, you can refer to website: www.kotlincn.net/docs/refere…

1.2. About Return

Now that we know about the implicit return in if, there’s a question here: Can I write a return explicitly? The answer is: no. Because in Kotlin, the semantics of a return look like this: it returns from the function that most directly surrounds it, or from an anonymous function.

Representations are not functions, so no, nor are Lambda expressions. No, there are special circumstances, so let’s talk about them:

To exit a Lambda expression, we must use a tag and forbid naked returns inside Lambda expressions because Lambda expressions cannot make the containing function return:

// forEach is a Lambda expression, and we use the ** tag ** to make it return
listOf(1.2.3.4.5).forEach lit@{
    if (it == 3) return@lit // Local returns to the caller of the lambda expression, namely the forEach loop
    print(it)
}
Copy the code

I can return directly in forEach. Yes, it can. Because forEach inline functions. Inline functions can return:

I’ll expand on inline functions later in this article.

Official: Inlining is allowed (this return (in a lambda expression, but exiting the function that contains it) is called a nonlocal return.)

inline fun inlined(block: () -> Unit) {
    println("hi!")}fun foo(a) {
    inlined {
        return // OK: The lambda expression is inline}}fun main(a) {
    foo()
}
Copy the code

For example, this return is legal because foreach is an inline function

fun hasZeros(ints: List<Int>): Boolean {ints. ForEach {if (it == 0) return true // return from hasZeros} return false} public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit { for (element in this) action(element) }Copy the code

Can you use it everyday? To be honest, no diao. However, we know how to explain the encounter, which is also a kind of gain.

2.1. When expression

When is an enhanced Plus version of the switch we all use. Its simplest form is as follows:

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else- > {// Notice this block
        print("x is neither 1 nor 2")}}Copy the code

We can also detect a value in (in) or not in (! In) an interval or set. That is, certain conditions are met:

when (x) {
    in 1.10. -> print("x is in the range")
    in validNumbers -> print("x is valid")!in 10.20. -> print("x is outside the range")
    else -> print("none of the above")}Copy the code

Another use is to detect whether a value is (is) or not (! Is) a value of a specific type.

Thanks to intelligent conversion, we can access methods and properties of this type without any additional detection.

fun hasPrefix(x: Any) = when(x) {
    is String -> x.startsWith("prefix")
    else -> false
}
Copy the code

That’s because “When” is switch plus. More interesting usage, welcome to leave a message to add yo ~


Messy little details

1.1, Class

val c = MyClass::class
Copy the code

The return is KClass. If we need Class, we need MyClass::class.java

1.2, this

To access this (a class or extension function, or a labeled function literal with a receiver) from an external scope we use this@label, where @label is a label that refers to the source of this:

class A { // Implicit tag @a
    inner class B { // Implicit tag @b
        fun Int.foo(a) { // The implicit tag @foo
            val a = this@A / / A of this
            val b = this@B / / B of this

            val c = this // The receiver of foo(), an Int
            val c1 = this@foo // The receiver of foo(), an Int

            val funLit = lambda@ fun String.(a) {
                val d = this // The recipient of funLit
            }


            val funLit2 = { s: String ->
                // the recipient of foo() because it contains the lambda expression
                // There is no receiver
                val d1 = this}}}}Copy the code

1.3. Anonymous Functions

var list = arrayListOf(1.2.3.4.5.6)
        // lambda expressions
        list.filter {
            it > 3
        }
        // a formal anonymous function
        list.filter(fun(it): Boolean {
            return it > 3
        })
        // An anonymous function short for automatic type inference
        list.filter(fun(it) = it > 3)
  
Copy the code

The return type is automatically inferred for anonymous functions with an expression body, whereas the return type with a code block body must be explicitly specified (or assumed to be Unit) that anonymous function arguments are always passed in parentheses. The shorthand syntax that allows functions to be left outside parentheses applies only to lambda expressions.

Another difference between Lambda expressions and anonymous functions is the behavior of non-local returns. An unlabeled return statement always returns in a function declared with the fun keyword. This means that a return in a lambda expression will return from the function that contains it, and a return in an anonymous function will return from the anonymous function itself.

1.4. Inline functions

Using higher-order functions (see my previous article on higher-order functions) has some run-time efficiency costs. Because every function is an object, it captures a closure (that is, variables that are accessed within the function body). We all know that memory allocation (for function objects and classes) and virtual calls introduce runtime overhead. So in this case, you need to have inline functions to eliminate that overhead. The lock function is used to explain the problem:

fun <T> lock(lock: Lock, body: () -> T): T {
  lock.lock()
  try {
    return body()
  }
  finally {
    lock.unlock()
  }
}
Copy the code

Normally we would call this function lock(l) {foo()}. If left unmodified, a large number of object pairs can be created because of the presence of higher-order functions, so we need to eliminate this extra by inlining.

// With inline, the code will compile like this
l.lock()
try {
    foo()
}
finally {
    l.unlock()
}
Copy the code

There may be some friends who are confused here and don’t understand what the inline is doing. Let’s use three functions to explain the problem:

// Within a function, another function is called. The virtual machine is bound to add a lot of overhead to the funtion1 call. Funtion (){var aa =1+1+1 var bb =1+1+1 var cc =1+1+1} funtion(){var aa =1+1+1 var bb =1+1+1 var cc =1+1+1} Funtion (){var a =1+1+1 var aa =1+1+1 var bb =1+1+1 var cc =1+1+1}Copy the code

We can see that, after being inlined, our implementation in funtion1 behaves as if it were in funtion. So you reduce the cost of this part.

Inlining may result in an increase in generated code; But if we do it right (that is, avoid inlining excessively large functions), performance can improve, especially at “megamorphic” calls in loops.

The end of the

OK, so that’s pretty much the end of Kotlin’s syntactic based talk. The rest of this article will basically focus on Kotlin in Android. I hope you can learn something from it

I am a fresh graduate, recently and friends maintain a public account, the content is that we in the transition from fresh graduate to the development of this way stepped on the pit, as well as our step by step learning records, if interested in friends can pay attention to it, together with fuel ~