Kotlin learning: basic grammar, basic data types, conditional control, loop control

Kotlin

Kotlin is a statically typed programming language that runs on the Java virtual machine. Kotlin can be compiled into Java bytecode or JavaScript, which is convenient for running on devices without JVM

Kotlin program files are in .kt

Why choose Kotlin?

  • Simplicity: Greatly reduce the amount of boilerplate code.
  • Security: Avoid the entire class of errors such as null pointer exceptions.
  • Interoperability: Make full use of the existing libraries of JVM, Android and browsers.
  • Tool-friendly: It can be built using any Java IDE or command line.

Kotlin basic syntax

Import
by default There are multiple packages that will be imported into each Kotlin file by default:

kotlin.*
kotlin.annotation.*
kotlin.collections.*
kotlin.comparisons.*
kotlin.io.*
kotlin.ranges.*
kotlin.sequences.*
kotlin.text.*

Function definition
Function definition uses the keyword fun, and the parameter format is: parameter: type

fun sum(a: Int, b: Int): Int {   // Int 参数,返回值 Int
    return a + b
}

Expression as the body of the function, the return type is automatically inferred

fun sum(a: Int, b: Int) = a + b
public fun sum(a: Int, b: Int): Int = a + b   // public 方法则必须明确写出返回类型

Functions with no return value (similar to void in Java):

fun printSum(a: Int, b: Int): Unit { 
    print(a + b)
}


// If it returns the Unit type, it can be omitted (the same is true for public methods):

public fun printSum(a: Int, b: Int) { 
    print(a + b)
}

Variable-length parameter function The variable-length parameter of a function
can be identified with the vararg keyword

fun vars(vararg v:Int){
    for(vt in v){
        print(vt)
    }
}

// 测试
fun main(args: Array<String>) {
    vars(1,2,3,4,5)  // 输出12345
}

Lambda (anonymous function)
Examples of using lambda expressions:

// 测试
fun main(args: Array<String>) {
    val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
    println(sumLambda(1,2))  // 输出 3
}

Define constants and variables
Variable variable definition: var keyword
var <identifier>: <type> = <initial value>

Definition of immutable variables: val keyword, variables that can only be assigned once (similar to final modified variables in Java)
val <identifier>: <type> = <initialization value>

Constants and variables can have no initialization value, but they must be initialized before being referenced

The compiler supports automatic type judgment, that is, the type may not be specified in the declaration, and the compiler judges it.

Comment
Kotlin supports single-line and multi-line comments.
Unlike Java, block comments in Kotlin allow nesting

The string template
$ represents a variable name or variable value
$varName represents the variable value
${varName.fun()} represents the return value of the variable method:

var a = 1
// Simple name in the template:

val s1 = "a is $a" 
a = 2
// 模板中的任意表达式:
val s2 = "${s1.replace("is", "was")}, but now is $a"

The null check mechanism of
Kotlin's null safety design. For parameters declared to be nullable, null judgment processing should be performed when using them. There are two processing methods, add after the field!! Throw a null exception like Java, add after the other field ? Can it not be processed and the return value is null or cooperate?: Short judgment processing

//类型后面加?表示可为空
var age: String? = "23" 
//抛出空指针异常
val ages = age!!.toInt()
//不做处理返回 null
val ages1 = age?.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1

When a reference may be a null value, the corresponding type declaration must be clearly marked as nullable type detection and automatic type conversion.
We can use the is operator to detect whether an expression is an instance of a certain type (similar to the instanceof in Java Keywords)

fun getStringLength(obj: Any): Int? {
  if (obj is String) {    //或者 !is
    // 做过类型判断以后,obj会被系统自动转换为String类型
    return obj.length 
  }

  // 这里的obj仍然是Any类型的引用
  return null
}

Range The
range expression is formed by the rangeTo function with operator form.. supplemented by in and !in.
Range is defined for any comparable type, but for integer primitive types, it has an optimized implementation
// Use step to specify the step long

for (i in 1..4 step 2) print(i) // 输出“13”

for (i in 4 downTo 1 step 2) print(i) // 输出“42”

// 使用 until 函数排除结束元素
for (i in 1 until 10) {   // i in [1, 10) 排除了 10
     println(i)
}

===============================================================================

Kotlin basic data types
Kotlin basic numeric types include Byte, Short, Int, Long, Float, Double, etc.
The difference from Java is that characters are not a numeric type, but an independent data type.
Type Bit width
Double 64
Float 32
Long 64
Int 32
Short 16
Byte 8

Literal constants
The following are all types of literal constants:
Decimal: 123
Long integer ends with an uppercase L: 123L
Hexadecimal starts with 0x: 0x0F
2 Base starts with 0b: 0b00001011
Note: Octal does not support

Kotlin also supports floating-point values ​​represented by traditional symbols:
Doubles Default writing: 123.5, 123.5e10
Floats use f or F suffix: 123.5f

You can use underscores to make numeric constants more readable
val oneMillion = 1_000_000

Comparing two numbers There
are no basic data types in Kotlin, only encapsulated number types. Every time you define a variable, Kotlin actually encapsulates an object for you, so that you can guarantee that there will be no null pointers.
The number type is also the same, so when comparing two numbers, there is a difference between comparing the size of the data and comparing whether the two objects are the same.

In Kotlin, three equal signs === means comparing the object address, and two == means comparing two values.

Type conversion
Due to different representations, smaller types are not subtypes of larger types, and smaller types cannot be implicitly converted to larger types. This means that we cannot assign a Byte value to an Int variable without explicit conversion.
Each data type has the following methods, which can be converted to other types

toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char

In some cases, automatic type conversion can also be used, provided that the correct data type can be inferred according to the context and the mathematical operators will be overloaded accordingly.
val l = 1L + 3 // Long + Int => Long

Bit operators
For Int and Long types, there are a series of bit operators that can be used

shl(bits) – 左移位 (Java’s <<)
shr(bits) – 右移位 (Java’s >>)
ushr(bits) – 无符号右移位 (Java’s >>>)
and(bits) – 与
or(bits) – 或
xor(bits) – 异或
inv() – 反向

Characters
are different from Java. Char in Kotlin cannot be directly manipulated with numbers. Char must be enclosed in single quotation marks. For example, ordinary characters '0','a'.
Character literals are enclosed in single quotes: '1'. Special characters can be escaped with backslashes. Support these escape sequences: \t, \b, \n, \r, \', \", \\ and \$. To encode other characters, use Unicode escape sequence syntax:'\uFF00'
can be explicit Convert characters to Int numbers

fun decimalDigitValue(c: Char): Int {
    if (c !in '0'..'9')
        throw IllegalArgumentException("Out of range")
    return c.toInt() - '0'.toInt() // 显式转换为数字
}

Boolean
Boolean is represented by the Boolean type, which has two values: true and false.

Nullable reference Boolean will be boxed if needed.
The built-in Boolean operations are

|| – 短路逻辑或
&& – 短路逻辑与
! - 逻辑非

Array
Array Array implemented by classes, and there is a size and attribute get and set methods, the use of [] overloads the get and set methods, so we can easily index obtained by setting a position or a value corresponding to the array. There are two ways to create an array: one is to use the function arrayOf(); the other is to use a factory function.

fun main(args: Array<String>) {
    //[1,2,3]
    val a = arrayOf(1, 2, 3)
    //[0,2,4]
    val b = Array(3, { i -> (i * 2) })

    //读取数组内容
    println(a[0])    // 输出结果:1
    println(b[1])    // 输出结果:2
}

Note: Unlike Java, arrays in Kotlin are invariant.
In addition to the class Array, there are also ByteArray, ShortArray, IntArray, which are used to represent various types of arrays, eliminating the need for boxing operations, so it is more efficient, and its usage is the same as Array:

val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]

Strings
Like Java, Strings are immutable. The square bracket [] syntax can be very convenient to get a character in a string, and it can also be traversed through a for loop

for (c in str) {
    println(c)
}

Kotlin supports strings with three quotation marks """ and multi-line strings

String can use trimMargin() method to remove extra white space.
Default | is used as boundary prefix, but you can select other characters and pass in as parameters, such as trimMargin(">")

String template
Strings can contain template expressions, that is, some small pieces of code, which will be evaluated and the result will be merged into the string. A template expression starts with a dollar sign ($) and consists of a simple name:
or any expression enclosed in curly braces:

fun main(args: Array<String>) {
    val s = "runoob"
    val str = "$s.length is ${s.length}" // 求值结果为 "runoob.length is 6"
    println(str)
}

Both native strings and escaped strings support templates internally. If you need to represent the literal $ character in a native string (it does not support backslash escaping), you can use the following syntax:

fun main(args: Array<String>) {
    val price = """
    ${'$'}9.99
    """
    println(price)  // 求值结果为 $9.99
}


================================================== ============================= Kotlin conditional control
if expression

// 传统用法
var max = a 
if (a < b) max = b

// 使用 else 
var max: Int
if (a > b) {
    max = a
} else {
    max = b
}
 
// 作为表达式
val max = if (a > b) a else b

We can also assign the result of the IF expression to a variable

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

This also shows that I don’t need a ternary operator like Java, because we can use it to simply implement

val c = if (condition) a else b

Use interval
Use the in operator to detect whether a number is within a specified interval, and the interval format is x..y

fun main(args: Array<String>) {
    val x = 5
    val y = 9
    if (x in 1..8) {
        println("x 在区间内")
    }
}

The when expression
when compares its parameters and all branch conditions in order until a branch meets the condition.

when can be used as an expression or as a statement. If it is used as an expression, the value of the qualified branch is the value of the entire expression. If it is used as a statement, the value of the individual branch is ignored.

when is similar to the switch operator in other languages. Its simplest form is as follows

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // 注意这个块
        print("x 不是 1 ,也不是 2")
    }
}

In when, else is the same as switch's default. If other branches do not meet the conditions, the else branch will be evaluated.
If many branches need to be processed in the same way, you can put multiple branch conditions together, separated by commas

when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

We can also detect whether a value is (in) or not (!in) in an interval or set

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

Another possibility is to detect whether a value is (is) or not (!is) a specific type of value. Note: Thanks to the smart conversion, you can access the methods and properties of this type without any additional testing.

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

when can also be used to replace if-else if chains. If no parameters are provided, all branch conditions are simple Boolean expressions, and when a branch condition is true, the branch is executed:

when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")
}


===============================================================================

Kotlin loop control
for loop for loop
for loop can traverse any object that provides iterator (iterator), the syntax is as follows

for (item in collection) print(item)
循环体可以是一个代码块
for (item: Int in ints) {
    // ……
}

As mentioned above, for can loop through any object that provides an iterator.

If you want to traverse an array or a list by index, you can do this

for (i in array.indices) {
    print(array[i])
}

Note that this "traversal on the interval" will be compiled into an optimized implementation without creating additional objects.

Or you can use the library function withIndex:

for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}

while and do...while loop
while is the most basic loop, its structure is

while( 布尔表达式 ) {
  //循环内容
}

do...while loop For while statements, if the conditions are not met, you cannot enter the loop. But sometimes we need to execute at least once even if the conditions are not met.

The do...while loop is similar to the while loop, the difference is that the do...while loop will be executed at least once

do {
       //代码语句
}while(布尔表达式);

Return and jump

Kotlin has three structured jump expressions:

  • return. By default, it returns from the function that most directly encloses it or the anonymous function.
  • break. Terminate the loop that most directly surrounds it.
  • continue. Continue to the next cycle that most directly surrounds it

Kotlin supports traditional break and continue operators in loops

fun main(args: Array<String>) {
    for (i in 1..10) {
        if (i==3) continue  // i 为 3 时跳过当前循环,继续下一次循环
        println(i)
        if (i>5) break   // i 为 6 时 跳出循环
    }
}

Break and Continue labels
Any expression in Kotlin can be marked with a label. The format of the label is the identifier followed by the @ symbol. For example, abc@ and fooBar@ are all valid labels. To label an expression, we just need to add a label before it

loop@ for (i in 1..100) {
    // ……
}

Now, we can restrict break or continue with labels

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (……) break@loop
    }
}


The label-limited break jumps to the execution point just after the loop specified by the label. continue continues the next iteration of the loop specified by the label


Kotlin returned at the label has function literals, local functions, and object expressions. So Kotlin functions can be nested. The label-restricted return allows us to return from the outer function. One of the most important uses is to return from lambda expressions. Recall when we wrote this

fun foo() {
    ints.forEach {
        if (it == 0) return
        print(it)
    }
}

This return expression returns from the function that most directly surrounds it, namely foo. (Note that this kind of non-local return only supports lambda expressions passed to inline functions.) If we need to return from a lambda expression, we must label it and limit the return

fun foo() {
    ints.forEach lit@ {
        if (it == 0) return@lit
        print(it)
    }
}

Now, it will only return from lambda expressions. It is usually more convenient to use implicit tags. The label has the same name as the function that accepts the lambda

fun foo() {
    ints.forEach {
        if (it == 0) return@forEach
        print(it)
    }
}

Or, we can substitute an anonymous function for the lambda expression. The return statement inside the anonymous function will return from the anonymous function itself

fun foo() {
    ints.forEach(fun(value: Int) {
        if (value == 0) return
        print(value)
    })
}

When returning a return value, the parser preferentially chooses the label-restricted return, that is,
return@a 1
means "return 1 from the label @a" instead of "return a label-labeled expression (@a 1)"
 

Guess you like

Origin blog.csdn.net/PrisonJoker/article/details/113281555