Author of this issue:

Video: Throwing line (Zhu Kai)

Sinyu (Shen Xinyu).

Hi, I’m Zhu Kai from throwline. This is the third (and final) part of the basics of Kotlin from last semester: The “Better things to Use” in Kotlin. Old friends don’t talk much, video first.

Because I never learned how to post a video in nuggets, so please clickhereGo to Bilibili. or clickhereGo to YouTube to see.

The following is from Sinyu, the author of the article.

In the last installment, we introduced some of the differences between the Java version of Kotlin. In this installment we’ll take it a step further and talk about the “more convenient” uses of Kotlin. You can write Kotlin normally if you don’t know it, but you’ll feel better if you get familiar with it.

The constructor

The main constructor

We’ve seen Kotlin’s constructor before:

🏝 ️class User {
    var name: String
    constructor(name: String) {
        this.name = name
    }
}
Copy the code

There’s an even easier way to write a constructor in Kotlin:

🏝 ️ 👇class User constructor(name: String) {
    // 👇 is the same as name in the constructor
    var name: String = name
}
Copy the code

There are a few differences:

  • constructorThe constructor is moved after the class name
  • Attributes of a classnameArguments in the constructor can be referencedname

This is called “primary constructor”. In contrast, constructors written in classes are referred to as “subconstructors” in part 2. In Kotlin, a class can have at most one primary constructor (or none), and there is no limit to the number of secondary constructors.

Parameters in the primary constructor can be used in the init code block in addition to the properties of the class:

🏝 ️class User constructor(name: String) {
    var name: String
    init {
        this.name = name
    }
}
Copy the code

The init code block is executed immediately after the main constructor, because the main constructor has no body of its own, so the init code block acts as the main constructor body.

In addition, if a class has a primary constructor, all other secondary constructors need to call the primary constructor through this keyword, either directly or indirectly through another secondary constructor. An error is reported if the IDE is not called:

🏝 ️class User constructor(var name: String) {
    constructor(name: String, id: Int) {
    // 👆 Primary constructor call expected}}Copy the code

Why force the secondary constructor to call the primary constructor when there is a primary constructor in the class?

We start with the properties of the primary constructor. Once the primary constructor is declared in a class, it contains two things:

  • Mandatory: No matter which constructor is used, the primary constructor is required to create objects of the class
  • Primary: During class initialization, the primary constructor is executed first

That’s how the main constructor is named.

When a class has both a primary constructor and a secondary constructor, write:

🏝 ️class User constructor(var name: String) {
                                   // 👇 👇 calls the main constructor directly
    constructor(name: String, id: Int) : this(name) {
    }
                                                // 👇 calls the primary constructor indirectly through the previous secondary constructor
    constructor(name: String, id: Int, age: Int) : this(name, id) {
    }
}
Copy the code

When an object is created using the secondary constructor, the init code block is executed before the secondary constructor. If you think of the primary constructor as the head of the body, the init code block is the neck, and the secondary constructor is equivalent to the rest of the body.

If you are careful, you may notice that the symbol appears again. It also appears in other situations, such as:

  • Variable declaration:var id: Int
  • Class inheritance:class MainActivity : AppCompatActivity() {}
  • Interface implementation:class User : Impl {}
  • Anonymous class creation:object: ViewPager.SimpleOnPageChangeListener() {}
  • The return value of the function:fun sum(a: Int, b: Int): Int

As you can see, symbols appear very frequently in Kotlin, and they actually represent a dependency, in this case a dependency on the main constructor.

Normally, the constructor keyword in the main constructor can be omitted:

🏝 ️class User(name: String) {
    var name: String = name
}
Copy the code

However, there are some scenarios where constructor should not be omitted, such as using “visibility modifiers” or “annotations” on the main constructor:

  • Visibility modifiers are used to modify ordinary functions in the same way as they are used to modify constructors. We won’t go into detail here:

    🏝 ️class User private constructor(name: String) {
    // 👆 The main constructor is decorated to be private so that it cannot be called externally
    }
    Copy the code
  • We’ll talk about annotations later, but we won’t go into that here

Since the primary constructor can simplify the initialization of a class, let’s do the same for the class, and use the primary constructor to simplify the initialization of attributes.

Properties are declared in the primary constructor

We’ve seen that parameters in the primary constructor can be assigned to properties, but you can also declare properties directly in the primary constructor:

🏝 ️ 👇class User(var name: String) {
}
// This is equivalent to:
class User(name: String) {
  var name: String = name
}
Copy the code

Adding var or val to the argument declaration of the primary constructor is equivalent to creating a property of that name in the class, and the initial value is the value of that parameter in the primary constructor.

With all that said about the main constructor, let’s summarize the class initializer:

  • First create a User class:

    🏝 ️class User {}Copy the code
  • Add a primary constructor with parameters name and id:

    🏝 ️class User(name: String, id: String) {
    }
    
    Copy the code
  • Declare the name and ID in the primary constructor as attributes of the class:

    🏝 ️class User(val name: String, val id: String) {
    }
    
    Copy the code
  • Then add some initialization logic to the init code block:

    🏝 ️class User(val name: String, valid: String) { init { ... }}Copy the code
  • Finally, add another subconstructor:

    🏝 ️class User(val name: String, val id: String) {
        init {
            ...
        }
        
        constructor(person: Person) : this(person.name, person.id) {
        }
    }
    
    Copy the code

    When a class has multiple constructors, just write the most basic and generic one as the primary constructor. Here we choose the constructor with parameters name and ID as the primary constructor.

At this point, the entire class is initialized in the same order as above.

In addition to constructors, there are many simplified ways to write ordinary functions.

Function to simplify

use=Connection return value

We already know how to write functions in Kotlin:

🏝 ️fun area(width: Int, height: Int): Int {
    return width * height
}

Copy the code

A function with only one line of code can also be written like this:

🏝 ️ 👇fun area(width: Int, height: Int): Int = width * height

Copy the code

{} and return are gone, using the = symbol to concatenate the return value.

As we mentioned earlier, Kotlin has a “type inference” feature, so the return type of this function can be hidden:

🏝 ️// 👇 omits the return type
fun area(width: Int, height: Int) = width * height

Copy the code

In practice, however, it is recommended to explicitly write the return type to increase code readability.

This is the case when the function returns a value. For the case where no value is returned, it can be interpreted as Unit:

🏝 ️fun sayHi(name: String) {
    println("Hi " + name)
}

Copy the code

So it can also be simplified as follows:

🏝 ️ 👇fun sayHi(name: String) = println("Hi " + name)

Copy the code

Having simplified the body of the function, let’s look at the arguments.

We’re all familiar with method overloading in Java. Is there a more convenient way to override methods in Kolin? Let’s look at the use of “parameter defaults” in Kotlin.

Parameter Default Value

In Java, multiple methods with the same name can be defined in a class, but the type or number of arguments must be different. This is called method overloading:

☕ ️public void sayHi(String name) {
    System.out.println("Hi " + name);
}

public void sayHi(a) {
    sayHi("world"); 
}

Copy the code

In Kotlin, function overloading can also be done in this way, but there is an even simpler way, which is the “parameter defaults” :

🏝 ️ 👇fun sayHi(name: String = "world") = println("Hi " + name)

Copy the code

Here the world is the default value for the name argument, which is used when the function is called without passing an argument.

This is equivalent to the overloaded method written in Java above, when the sayHi function is called, the argument is optional:

🏝 ️ sayHi ("kaixue.io")
sayHi() // Use the default value "world"

Copy the code

What are the benefits of parameter defaults in Kotlin, since they have the same effect as overloaded functions? Did you just write less code?

In Java, the internal implementation of each overloaded method can be different, which makes it impossible to guarantee consistency within the design of overloaded methods, and Kotlin’s parameter defaults solve this problem.

However, parameter defaults are not completely arbitrary when invoked.

Take a look at the following code where arguments with default values precede arguments with no default values:

🏝 ️fun sayHi(name: String = "world", age: Int){... } sayHi(10)
// 👆 If you want to use the default, the IDE will report the following two errors
// The integer literal does not conform to the expected type String
// No value passed for parameter 'age'

Copy the code

This error tells you that the parameters do not match, indicating that we are “opening” the wrong way. In fact, Kotlin fixes this problem by “naming parameters”.

Named parameters

Specific usage is as follows:

🏝 ️fun sayHi(name: String = "world", age: Int) {
    ...
}
      👇   
sayHi(age = 21)

Copy the code

When the function is called, the name of the parameter age is explicitly specified, which is called a named parameter. Each function parameter in Kotlin can be used as a named parameter.

Let’s look at another example of a function with very many arguments:

🏝 ️fun sayHi(name: String = "world", age: Int, isStudent: Boolean = true, isFat: Boolean = true, isTall: Boolean = true){... }Copy the code

When a function has too many arguments, a call to the function looks like this:

🏝 ️ sayHi ("world".21.false.true.false)

Copy the code

When we look at the long list of booleans behind it, it is hard to distinguish the use of each parameter, and the readability is poor. By naming parameters, we can write:

🏝 ️ sayHi (name ="wo", age = 21, isStudent = false, isFat = true, isTall = false)

Copy the code

The opposite concept of named parameters is called positional parameters, which are filled in in positional order.

When a function is called, if positional arguments are mixed with named arguments, all positional arguments should precede the first named argument:

🏝 ️fun sayHi(name: String = "world", age: Int){... } sayHi(name ="wo".21) // 👈 IDE can offer error, puzzle named and tourists is not allowed
sayHi("wo", age = 21) // 👈 this is the correct way

Copy the code

With named parameters out of the way, let’s take a look at another common function in Kotlin: nested functions.

Local functions (nested functions)

Let’s start with this code, which is a simple login function:

🏝 ️fun login(user: String, password: String, illegalStr: String) {
    // Verify that user is null
    if (user.isEmpty()) {
        throw IllegalArgumentException(illegalStr)
    }
    // Verify that the password is empty
    if (password.isEmpty()) {
        throw IllegalArgumentException(illegalStr)
    }
}

Copy the code

The argument checking part of this function is a bit redundant, and we don’t want to expose this logic as a separate function. You can use nested functions to declare a function inside the login function:

🏝 ️fun login(user: String, password: String, illegalStr: String){👇fun validate(value: String, illegalStr: String) {
      if (value.isEmpty()) {
          throw👇 validate(user, illegalStr) validate(password, illegalStr)} 👇 validate(user, illegalStr) validate(password, illegalStr)}Copy the code

Here we put our common validation logic into the nested function validate, which is inaccessible to anyone other than the login function.

The illegalStr is passed as an argument to the nested function, which is unnecessary because the nested function has access to all variables or constants outside of it, such as attributes in the class, parameters and variables in the current function, and so on.

Let’s make some improvements:

🏝 ️fun login(user: String, password: String, illegalStr: String) {
    fun validate(value: String) {
        if(value. IsEmpty ()) {👇throw IllegalArgumentException(illegalStr)
        }
    }
    ...
}

Copy the code

The illegalStr parameter in the nested function is omitted and the illegalStr parameter of the outer login function is used directly within the nested function.

There is another, simpler way to use the validation logic in the login function above:

🏝 ️fun login(user: String, password: String, illegalStr: String) {
    require(user.isNotEmpty()) { illegalStr }
    require(password.isNotEmpty()) { illegalStr }
}

Copy the code

Lambda expressions are used as well as Kotlin’s built-in require function, which will not be expanded here but will be covered in future articles.

string

So, once we’ve done a simplified way of writing ordinary functions, there’s also a lot of handy ways to write strings in Kotlin.

String template

In Java, strings and variables are concatenated using the + symbol, as is the case in Kotlin:

🏝 ️val name = "world"
println("Hi " + name)

Copy the code

However, when there are many variables, the readability becomes worse and writing becomes more difficult.

The Java solution is string.format:

☕ ️ System. Out. Print (String format ("Hi %s", name));

Copy the code

Kotlin gives us a more convenient way to write:

🏝 ️val name = "world"
// 👇 uses the '$' sign as an argument
println("Hi $name")

Copy the code

This is done by changing the name from the back to the front, simplifying the code while increasing the readability of the string.

$can also be followed by an expression, but an expression is a whole, so we’ll wrap it around {} :

🏝 ️val name = "world"
println("Hi ${name.length}") 

Copy the code

Just like the parentheses of the four operations, the syntactic priority is raised, and the {} can be omitted for the single variable scenario.

String templates also support escape characters, such as newlines with the escape character \n:

🏝 ️val name = "world! \n"
println("Hi $name") // 👈 will have an extra blank line

Copy the code

The use of string templates is familiar to Android engineers.

First, the Groovy language used by Gradle already has this support:

def name = "world"
println "Hi ${name}"

Copy the code

In Android’s resource files, define strings are used similarly:

<string name="hi">Hi %s</string> 

Copy the code
☕ ️ get string (R.i d.h. I,"world");

Copy the code

Raw String (native string)

Sometimes we don’t want to write too many escape characters, and Kotlin uses “native strings” to do this.

The usage is to enclose a string with a pair of “” :

🏝 ️val name = "world"
val myName = "kotlin"
           👇
val text = """
      Hi $name!
    My name is $myName.\n
"""
println(text)

Copy the code

Here are a few caveats:

  • \nIt will not be escaped
  • The final output is exactly the same as what was written, including the actual newline
  • $Symbolic reference variables are still in effect

This is called a “native string.” The following output is displayed:

      Hi world!
    My name is kotlin.\n

Copy the code

But the alignment doesn’t look too elegant. Native strings can also be stripped of whitespace before each line with the trimMargin() function:

🏝 ️val text = "" "👇 | Hi world! |My name is kotlin. """.trimMargin()
println(text)

Copy the code

The following output is displayed:

Hi world!
My name is kotlin.

Copy the code

The trimMargin() function here has the following caveats:

  • |The symbol is the default boundary prefix, preceded only by a space, otherwise it will not take effect
  • When the output|The symbol and the space before it are removed
  • Boundary prefixes can also use other characters, such astrimMargin("/"), except that the above code uses the invocation of the parameter default values

I’ll leave strings there, but let’s look at some of the more convenient things we can do with arrays and collections.

Arrays and collections

Operators for arrays and collections

In previous articles, we learned the basic concepts of arrays and collections, but Kotlin also provides a number of functions that make it easier to manipulate arrays and collections.

First declare the following IntArray and List:

🏝 ️val intArray = intArrayOf(1.2.3)
val strList = listOf("a"."b"."c")

Copy the code

Next, the operator functions are explained:

  • ForEach: Iterate over each element

    🏝 ️// 👇 lambda expression where I represents each element of the array
    intArray.forEach { i ->
        print(i + "")}// Output: 1, 2, 3
    
    Copy the code

    In addition to “lambda” expressions, the concept of “closures” is also used here, which is a topic for another time.

  • Filter: Filter each element. If the conditions in the lambda expression are true, the element is left; otherwise, it is removed and a new set is generated

    🏝 ️/ / [1, 2, 3]⬇ ️//  {2, 3}
    
    // 👇 notice that this becomes a List
    valnewList: List = intArray.filter { i -> i ! =1 // 👈 filters out elements in the array that are equal to 1
    }
    
    Copy the code
  • Map: Iterates through each element and executes the given expression, eventually forming a new collection

    🏝 ️//  [1, 2, 3]⬇ ️//  {2, 3, 4}
    
    val newList: List = intArray.map { i ->
        i + 1 // 👈 increments each element by 1
    }
    
    Copy the code
  • FlatMap: Iterates over each element, creates a new collection for each element, and finally merges into a collection

    🏝 ️/ / [1, 2, 3]⬇ ️// {"2", "a" , "3", "a", "4", "a"}
    
    intArray.flatMap { i ->
        listOf("${i + 1}"."a") // 👈 generates a new collection
    }
    
    Copy the code

Why is filter changed to List

In this case, the array intArray, the collection strList also has these operations. Kotlin has many similar operation functions, which are not listed here.

In addition to arrays and collections, there is another data type commonly used in Kotlin: Range.

Range

There is no concept of Range in the Java language. Kotlin’s Range means Range. The interval is usually written as follows:

🏝 ️ 👇 👇val range: IntRange = 0.1000. 

Copy the code

The 0 here.. 1000 is the range from 0 to 1000, including 1000, mathematically known as the closed interval [0, 1000]. In addition to the IntRange here, we have CharRange and LongRange.

There is no definition of pure open interval in Kotlin, but there is a definition of half-open interval:

🏝 ️ 👇val range: IntRange = 0 until 1000 

Copy the code

Here, 0 until 1000 means from 0 to 1000, but not including 1000, which is the half-open interval [0, 1000].

Range is a thing that’s built for traversal:

🏝 ️val range = 0.1000.
// 👇 The default step is 1, and the output is 0, 1, 2, 3, 4, 5, 6, 7.... In 1000,
for (i in range) {
    print("$i,")}Copy the code

The in keyword here can be used in conjunction with the for loop to iterate over the values in range one by one. The use of the for loop control is explained later in this article.

Instead of using the default step size of 1, you can set the step size using step:

🏝 ️val range = 0.1000.
//               👇 步长为 2,输出:0, 2, 4, 6, 8, 10,....1000,
for (i in range step 2) {
    print("$i,")}Copy the code

Kotlin also provides a decrement interval downTo, but decrement does not have the use of a half-open interval:

🏝 ️// 👇 output: 4, 3, 2, 1,
for (i in 4 downTo 1) {
    print("$i,")}Copy the code

Where 4 downTo 1 represents the closed interval of decline [4, 1]. The downTo here and the step above are called infix expressions, which will be covered in a future article.

Sequence

Since we were familiar with the basic concepts of sequences in the previous installment, let’s take a closer look at how sequences are used.

Sequence a Sequence is also called a lazy set operation. To understand what is lazy, we can use the following example:

🏝 ️val sequence = sequenceOf(1.2.3.4)
val result: List = sequence
    .map { i ->
        println("Map $i")
        i * 2 
    }
    .filter { i ->
        println("Filter $i")
        i % 3= =0} 👇 println (result. The first ())// 👈 takes only the first element of the set

Copy the code
  • The concept of inertia first means that code running before the “👇” annotation is not executed immediately, it just defines a process of execution that is executed only when result is used

  • When the println of “👇” is executed, the data processing flow looks like this:

    • Select element 1 -> map as 2 -> filter to determine whether 2 is divisible by 3
    • Select element 2 -> map as 4 -> filter to determine whether 4 is divisible by 3
    • .

    Lazy means that when the first element meets the condition, Sequence will not perform the following element traversal, that is, the traversal of 4 will be skipped.

A List is not lazy:

🏝 ️val list = listOf(1.2.3.4)
val result: List = list
    .map { i ->
        println("Map $i")
        i * 2 
    }
    .filter { i ->
        println("Filter $i")
        i % 3= =0} 👇 println (result. The first ())// 👈 takes only the first element of the set

Copy the code

There are two points:

  • Execute immediately after the declaration
  • The data processing process is as follows:
    • {1, 2, 3, 4} -> {2, 4, 6, 8}
    • Iterate to see if it’s divisible by 3

The lazy load-like implementation of Sequence has the following advantages:

  • Once the conditions for traversal exit are met, subsequent unnecessary traversals can be omitted.
  • likeListThis implementation可迭代A collection class of interfaces that generates a new one each time the function is called可迭代And the next function is based on the new可迭代Execution, each function call generated temporarily可迭代Will result in additional memory consumption, whileSequenceThere’s only one in the whole process.

Therefore, Sequence data type can be used as a solution for streaming processing when the amount of data is relatively large or unknown.

Under controlled conditions

Compared with Java conditional control, Kotlin has made many optimizations and improvements to conditional control.

if/else

Let’s start with the Java if/else notation:

☕ ️int max;
if (a > b) {
    max = a;
} else {
    max = b;
}

Copy the code

In Kotlin, this is of course possible, but the if statement in Kotlin can also be assigned as an expression:

🏝 ️ 👇val max = if (a > b) a else b

Copy the code

In addition, Kotlin deprecates the ternary operator (condition? Then: otherwise), but we can use if/else instead.

The if/else branch above is a variable, which can also be a code block, and the last line of the code block is returned as the result:

🏝 ️val max = if (a > b) {
    println("max:a")
    a // 👈 returns a
} else {
    println("max:b")
    b // 👈 returns b
}

Copy the code

when

In Java, the switch statement is used to determine whether a variable is equal to a value in a series of values:

☕ ️switch (x) {
    case 1: {
        System.out.println("1");
        break;
    }
    case 2: {
        System.out.println("2");
        break;
    }
    default: {
        System.out.println("default"); }}Copy the code

In Kotlin it becomes when:

🏝 ️ 👇when(x) {👇1 -> { println("1")}2 -> { println("2")} 👇else -> { println("else")}}Copy the code

The differences here compared to Java are:

  • omittedcasebreakThe former is easier to understand, while the latter means that Kotlin automatically adds to each branchbreakTo prevent us from writing errors like Java
  • The default branch in Java usesdefaultKeyword, which is used in Kotlinelse

Like if/else, when can be used as an expression, with the result of the last line in the branch as the return value. Note that there must be an else branch so that the result will return anyway, unless all cases have been listed:

🏝 ️val value: Int = when (x) {
    1 -> { x + 1 }
    2 -> { x * 2 }
    else -> { x + 5}}Copy the code

In Java, when executing the same code in multiple cases, you can write:

☕️ switch (x) {case1:
    case 2: {
        System.out.println("x == 1 or x == 2");
        break;
    }
    default: {
        System.out.println("default"); }}Copy the code

In Kotlin, when multiple cases execute the same code, multiple branch conditions can be placed together, separated by, to indicate that these cases will execute the following code:

🏝 ️when(x) {👇1.2 -> print("x == 1 or x == 2")
    else -> print("else")}Copy the code

In the WHEN statement, we can also use expressions as criteria for branching:

  • Use in to check whether an interval or set is present:

    🏝 ️when(x) {👇in 1.10. -> print("X in the interval 1.. 10")
       👇
        in listOf(1.2) -> print("X is in the set.")
       👇 // not in
        !in 10.20. -> print("X is not in the interval 10.. In the 20")
        else -> print("Not on any interval.")}Copy the code
  • Or use IS for a specific type of detection:

    🏝 ️val isString = when(x) {👇is String -> true
        else -> false
    }
    
    Copy the code
  • We can also omit the argument after when, and each branch condition can be a Boolean expression:

    🏝 ️when{👇 str1. Contains ("a") -> print("String str1 contains a") 👇 str2. Length = =3 -> print("String str2 has a length of 3")}Copy the code

When a branch is evaluated by an expression, the branch’s code block is executed if the condition is true first.

for

We know that Java can iterate over a collection or array like this:

☕️
int[] array = {1.2.3.4};
for (int item : array) {
    ...
}

Copy the code

Kotlin’s array traversal looks like this:

🏝 ️val array = intArrayOf(1.2.3.4)
          👇
for (item in array) {
    ...
}

Copy the code

There are several differences with Java:

  • In Kotlin, representing a single elementitemDo not explicitly declare a type
  • Kotlin usesinIndicates the keyworditemarrayOne of the elements

Alternatively, the variable after Kotlin’s in can be any object that implements the Iterable interface.

In Java, we can also write for loops like this:

☕ ️for (int i = 0; i <= 10; i++) {
    // iterate from 0 to 10
}

Copy the code

But Kotlin didn’t write it that way, so how do you do a 0 to 10 traversal?

In fact, using the above mentioned interval can be implemented, the code is as follows:

🏝 ️for (i in 0.10.) {
    println(i)
}

Copy the code

try-catch

Try -catch: Kotlin () : Kotlin () : Kotlin () : Kotlin

🏝 ️try{... }catch (e: Exception) {
    ...
}
finally{... }Copy the code

You can see that Kotlin exception handling is basically the same as Java exception handling, with a few differences:

  • We know that in Java, when we call a method that throws an exception, we need to handle the exception otherwise we get an error:

    ☕ ️public class User{
        void sayHi(a) throws IOException {}void test(a) {
            sayHi();
            // 👆 IDE error, Unhandled exception: java.io.IOException}}Copy the code

    But in Kotlin, when calling the sayHi method of the User class above:

    🏝 ️val user = User()
    user.sayHi() // 👈 the IDE does not report an error when called normally, but an error occurs at runtime
    
    Copy the code

    Why don’t I get an error here? Because exceptions in Kotlin are not checked, they will only fail at run time if sayHi throws an exception.

  • A try-catch statement in Kotlin can also be an expression that allows the last line of a code block to be returned:

    🏝 ️ 👇val a: Int? = try { parseInt(input) } catch (e: NumberFormatException) { null }
    
    Copy the code

? .? :

We’ve talked about Kotlin’s space safety in a previous article, but there’s another common compound notation that makes space detection easier. Is that the Elvis operator? :.

We know about empty security calls, right? Later calls are executed if the object is not empty, and null is returned if the object is empty. If the expression is then assigned to a non-nullable variable:

🏝 ️val str: String? = "Hello"
var length: Int= str? .length// 👆, IDE error, Type Mismatch. Required: int.found :Int?

Copy the code

The reason for the error is that we have no value to return to length when STR is null

Now you can use the Elvis operator from Kotlin, right? For the bottom:

🏝 ️val str: String? = "Hello"
                             👇
val length: Int= str? .length ? :- 1

Copy the code

It means if the left hand side expression STR? If the. Length result is empty, -1 is returned.

There is another common use of the Elvis operator, as follows:

🏝 ️fun validate(user: User) {
    valid = user.id ? :return // 👈 Verifies whether user.id is empty. If so, return
}

/ / is equivalent to

fun validate(user: User) {
    if (user.id == null) {
        return
    }
    val id = user.id
}

Copy the code

Now that you have a deeper understanding of Kotlin’s air safety, let’s look at Kotlin’s equality comparator.

= == = =

== equals() == equals() == equals() == equals() == equals() == equals() == equals() == equals() == equals() == equals() == equals() == equals()

☕ ️ strings str1 ="123", str2 = "123";
System.out.println(str1.equals(str2));
System.out.println(str1 == str2); 

Copy the code

Kotlin also has two ways of comparing equals:

  • = =: can be used for basic data types as wellStringType for content comparison, equivalent to Javaequals
  • = = =: Compares referenced memory addresses, equivalent to Java= =

You can see that equals in Java corresponds to == in Kotlin, which makes our code much cleaner.

Here’s another code example:

🏝 ️val str1 = "123"
val str2 = "123"
println(str1 == str2) // 👈 equal contents, output: true

val str1= "String"
val str2 = str1
val str3 = str1
print(str2 === str3) // 👈 reference address equal, output: true

Copy the code

In fact, the equals function in Kotlin is an overload of the == operator, which is not covered here but will be covered in a later article.

exercises

  1. Implement one according to the following requirementsStudentClass:
    • Write out three constructors, one of which must be the primary constructor
    • Parameters in the primary constructor as properties
    • Let me write a normal functionshowRequires the output of attributes in a class through a string template
  2. Write a program that uses today’s operators to find all elements in the set {21, 40, 11, 33, 78} that are divisible by 3, and print it.

The authors introduce

Video author

Throwing line (Zhu Kai)
  • Code school founder, project manager, content module planner and video content writer.
  • Android GDE (Google Certified Android Development Expert), former Flipboard Android Engineer.
  • GitHub Java ranks 92nd in the world, 6.6 K followers and 9.9 K stars on GitHub
  • MaterialEditText, a personal Android open source library, has been cited by several projects around the world, including Flipboard, a news-reading app with 500 million users worldwide.
  • I have been an Android instructor in Google Developer Group Beijing offline sharing sessions for several times.
  • After the release of the personal technical article “RxJava Details for Android Developers”, it was forwarded and shared in many domestic companies and teams and served as the main source of information for team technical meetings, as well as reverse spread to some Chinese teams in Some American companies such as Google and Uber.
  • HenCoder, the Android advanced teaching website founded by HenCoder, enjoys considerable influence in the Global Chinese Android development community.
  • After that, he founded HenCoder Plus, an Android advanced development teaching course, with students all over the world, including well-known first-tier Internet companies such as Ali, Toutiao, Huawei and Tencent, as well as senior software engineers from China’s Taiwan, Japan and the United States.

The authors

Sinyu, Shen Xinyu

Sinyu (Shen Xinyu) is an Instant Android engineer. In 2019, I joined Jike, participated in product iteration of Jike 6.0, and was responsible for infrastructure construction in Central Taiwan. Independently developed and operated an App with over 10,000 users.