MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Kotlin基础语法入门

2024-10-212.9k 阅读

Kotlin基本语法元素

变量声明

在Kotlin中,变量声明使用 var 关键字表示可变变量,使用 val 关键字表示不可变变量(类似于Java中的 final 变量)。例如:

var mutableVariable: Int = 10
val immutableVariable: String = "Hello, Kotlin!"

这里,mutableVariable 是一个可变的整数变量,immutableVariable 是一个不可变的字符串变量。Kotlin 支持类型推断,所以在很多情况下可以省略类型声明:

var number = 20
val message = "Type inference in action"

数据类型

Kotlin 的数据类型分为基本数据类型和引用数据类型。基本数据类型包括 ByteShortIntLongFloatDoubleBooleanChar

例如,整数类型 Int

val num1: Int = 100
val num2 = 200 // 类型推断为Int

浮点数类型 FloatDouble

val floatNum: Float = 3.14f
val doubleNum = 2.71828 // 类型推断为Double

布尔类型 Boolean

val isTrue: Boolean = true
val isFalse = false

字符类型 Char

val charA: Char = 'A'

引用数据类型如 String、数组、类等。String 在 Kotlin 中使用非常方便,支持字符串模板:

val name = "John"
val greeting = "Hello, $name!"
println(greeting)

运算符

算术运算符

Kotlin 提供了常见的算术运算符,如 +(加法)、-(减法)、*(乘法)、/(除法)和 %(取模)。

val a = 10
val b = 3
val sum = a + b
val difference = a - b
val product = a * b
val quotient = a / b
val remainder = a % b
println("Sum: $sum, Difference: $difference, Product: $product, Quotient: $quotient, Remainder: $remainder")

比较运算符

比较运算符用于比较两个值,包括 ==(等于)、!=(不等于)、>(大于)、<(小于)、>=(大于等于)和 <=(小于等于)。

val num1 = 10
val num2 = 15
println(num1 == num2)
println(num1 != num2)
println(num1 > num2)
println(num1 < num2)
println(num1 >= num2)
println(num1 <= num2)

逻辑运算符

逻辑运算符有 &&(逻辑与)、||(逻辑或)和 !(逻辑非)。

val condition1 = true
val condition2 = false
println(condition1 && condition2)
println(condition1 || condition2)
println(!condition1)

流程控制语句

if 语句

if 语句用于条件判断。在 Kotlin 中,if 可以作为表达式,有返回值。

val num = 10
val result = if (num > 5) {
    "Greater than 5"
} else {
    "Less than or equal to 5"
}
println(result)

也可以使用传统的 if - else if - else 结构:

val score = 85
if (score >= 90) {
    println("A")
} else if (score >= 80) {
    println("B")
} else if (score >= 70) {
    println("C")
} else {
    println("D")
}

when 语句

when 语句类似于 Java 中的 switch - case,但功能更强大。它可以接受各种类型的参数。

val day = 3
val dayName = when (day) {
    1 -> "Monday"
    2 -> "Tuesday"
    3 -> "Wednesday"
    4 -> "Thursday"
    5 -> "Friday"
    6 -> "Saturday"
    7 -> "Sunday"
    else -> "Invalid day"
}
println(dayName)

when 还可以用于更复杂的条件判断,例如:

val num = 15
when {
    num % 2 == 0 -> println("$num is even")
    num % 2 != 0 -> println("$num is odd")
}

for 循环

for 循环用于遍历集合或执行固定次数的操作。遍历数组:

val numbers = intArrayOf(1, 2, 3, 4, 5)
for (number in numbers) {
    println(number)
}

遍历区间:

for (i in 1..5) {
    println(i)
}

这里 1..5 表示从 1 到 5 的闭区间。如果要创建开区间,可以使用 until,例如 1 until 5 表示从 1 到 4。

while 和 do - while 循环

while 循环在条件为真时执行循环体:

var count = 0
while (count < 5) {
    println(count)
    count++
}

do - while 循环先执行一次循环体,然后再检查条件:

var num1 = 0
do {
    println(num1)
    num1++
} while (num1 < 5)

函数

函数定义

在 Kotlin 中,函数使用 fun 关键字定义。函数可以有参数和返回值。

fun add(a: Int, b: Int): Int {
    return a + b
}

这里定义了一个名为 add 的函数,接受两个 Int 类型的参数,并返回它们的和。如果函数体只有一行代码,可以使用简洁的写法:

fun multiply(a: Int, b: Int) = a * b

函数参数

函数参数可以有默认值,这样在调用函数时可以省略这些参数。

fun greet(name: String, message: String = "Hello") {
    println("$message, $name!")
}
greet("John")
greet("Jane", "Hi")

可变参数

使用 vararg 关键字可以定义接受可变数量参数的函数。

fun sum(vararg numbers: Int): Int {
    var total = 0
    for (number in numbers) {
        total += number
    }
    return total
}
val result1 = sum(1, 2, 3)
val result2 = sum(10, 20, 30, 40)

类与对象

类定义

在 Kotlin 中,使用 class 关键字定义类。类可以包含属性和方法。

class Person {
    var name: String = ""
    var age: Int = 0

    fun introduce() {
        println("My name is $name and I'm $age years old.")
    }
}

这里定义了一个 Person 类,有 nameage 两个属性,以及 introduce 方法。

对象创建

创建类的对象很简单,直接使用类名加括号:

val person1 = Person()
person1.name = "Alice"
person1.age = 25
person1.introduce()

构造函数

类可以有主构造函数和次构造函数。主构造函数是类头的一部分:

class Rectangle(val width: Int, val height: Int) {
    fun area() = width * height
}
val rect = Rectangle(5, 10)
println("Rectangle area: ${rect.area()}")

这里主构造函数接受 widthheight 两个参数,并将它们声明为属性。

次构造函数使用 constructor 关键字定义:

class Circle {
    var radius: Double = 0.0

    constructor(radius: Double) {
        this.radius = radius
    }

    constructor() : this(1.0) {
    }

    fun area() = Math.PI * radius * radius
}
val circle1 = Circle(5.0)
val circle2 = Circle()
println("Circle 1 area: ${circle1.area()}")
println("Circle 2 area: ${circle2.area()}")

继承

类继承

在 Kotlin 中,使用 open 关键字修饰可以被继承的类,子类使用 : 符号继承父类。

open class Animal {
    open fun makeSound() {
        println("Animal makes a sound")
    }
}

class Dog : Animal() {
    override fun makeSound() {
        println("Dog barks")
    }
}

val dog = Dog()
dog.makeSound()

这里 Animal 是一个开放类,Dog 类继承自 Animal 并重写了 makeSound 方法。

访问修饰符

Kotlin 有四种访问修饰符:public(默认)、privateprotectedinternal

  • public:所有地方都可以访问。
  • private:只能在声明它的类内部访问。
  • protected:在类内部和子类中可以访问。
  • internal:在同一个模块内可以访问。

例如:

class MyClass {
    private var privateProperty = "Private property"
    protected var protectedProperty = "Protected property"
    internal var internalProperty = "Internal property"
    public var publicProperty = "Public property"

    private fun privateMethod() {
        println("This is a private method")
    }

    protected fun protectedMethod() {
        println("This is a protected method")
    }

    internal fun internalMethod() {
        println("This is an internal method")
    }

    public fun publicMethod() {
        println("This is a public method")
    }
}

接口

接口定义

使用 interface 关键字定义接口,接口可以包含抽象方法和默认实现的方法。

interface Shape {
    fun area(): Double
    fun perimeter(): Double

    fun printInfo() {
        println("Area: ${area()}, Perimeter: ${perimeter()}")
    }
}

这里 areaperimeter 是抽象方法,printInfo 是有默认实现的方法。

接口实现

类通过 : 符号实现接口,并实现接口中的抽象方法。

class Square(val side: Double) : Shape {
    override fun area() = side * side
    override fun perimeter() = 4 * side
}

val square = Square(5.0)
square.printInfo()

集合

列表(List)

Kotlin 中的列表是有序的元素集合。不可变列表使用 listOf 创建,可变列表使用 mutableListOf 创建。

val immutableList = listOf(1, 2, 3, 4)
val mutableList = mutableListOf(5, 6, 7, 8)
mutableList.add(9)
println(immutableList)
println(mutableList)

集合(Set)

集合是不包含重复元素的集合。不可变集合使用 setOf 创建,可变集合使用 mutableSetOf 创建。

val immutableSet = setOf(1, 2, 2, 3)
val mutableSet = mutableSetOf(4, 5, 5, 6)
mutableSet.add(7)
println(immutableSet)
println(mutableSet)

映射(Map)

映射是键值对的集合。不可变映射使用 mapOf 创建,可变映射使用 mutableMapOf 创建。

val immutableMap = mapOf("one" to 1, "two" to 2)
val mutableMap = mutableMapOf("three" to 3, "four" to 4)
mutableMap.put("five", 5)
println(immutableMap)
println(mutableMap)

异常处理

try - catch - finally 块

Kotlin 使用 try - catch - finally 块来处理异常。

try {
    val result = 10 / 0
    println(result)
} catch (e: ArithmeticException) {
    println("Caught an arithmetic exception: ${e.message}")
} finally {
    println("This is the finally block")
}

抛出异常

使用 throw 关键字抛出异常。

fun divide(a: Int, b: Int): Int {
    if (b == 0) {
        throw ArithmeticException("Cannot divide by zero")
    }
    return a / b
}

try {
    val result = divide(10, 0)
    println(result)
} catch (e: ArithmeticException) {
    println("Caught exception: ${e.message}")
}

Lambda 表达式与高阶函数

Lambda 表达式

Lambda 表达式是一个匿名函数,它可以作为参数传递给其他函数。

val sum: (Int, Int) -> Int = { a, b -> a + b }
val result = sum(3, 5)
println(result)

这里定义了一个 sum 的 Lambda 表达式,它接受两个 Int 参数并返回它们的和。

高阶函数

高阶函数是接受其他函数作为参数或返回一个函数的函数。

fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

val addLambda: (Int, Int) -> Int = { x, y -> x + y }
val subLambda: (Int, Int) -> Int = { x, y -> x - y }

val addResult = operate(10, 5, addLambda)
val subResult = operate(10, 5, subLambda)
println("Add result: $addResult, Sub result: $subResult")

这里 operate 是一个高阶函数,它接受两个整数和一个操作函数作为参数,并返回操作结果。

通过以上对 Kotlin 基础语法的介绍,你应该对 Kotlin 有了一个初步的了解,可以开始编写简单的 Kotlin 程序了。随着进一步深入学习,你会发现 Kotlin 更多强大的特性和应用场景。例如在 Android 开发中,Kotlin 已经成为官方推荐的编程语言,其简洁的语法和丰富的特性大大提高了开发效率。在后续的学习中,可以探索 Kotlin 的泛型、扩展函数、协程等更高级的特性,以充分发挥 Kotlin 的潜力。

泛型

泛型类

在 Kotlin 中,我们可以定义泛型类,使其能够处理不同类型的数据。泛型类型参数用尖括号 <> 括起来。

class Box<T>(val value: T) {
    fun getValue(): T {
        return value
    }
}
val intBox = Box(10)
val stringBox = Box("Hello")
println(intBox.getValue())
println(stringBox.getValue())

这里的 Box 类是一个泛型类,T 是类型参数。我们可以创建不同类型的 Box 对象,intBox 存储 Int 类型的值,stringBox 存储 String 类型的值。

泛型函数

除了泛型类,还可以定义泛型函数。

fun <T> printValue(value: T) {
    println("The value is: $value")
}
printValue(15)
printValue("World")

printValue 函数是一个泛型函数,它可以接受任何类型的参数并打印出来。

泛型约束

有时候,我们希望对泛型类型参数进行一些限制,这就需要用到泛型约束。例如,我们可能希望某个泛型类型必须是某个类的子类。

open class NumberHolder<T : Number>(val number: T) {
    fun getNumber(): T {
        return number
    }
}
val intHolder = NumberHolder(20)
val doubleHolder = NumberHolder(3.14)
// 下面这行代码会报错,因为String不是Number的子类
// val stringHolder = NumberHolder("Not a number")

这里 T : Number 表示 T 必须是 Number 类或其子类。

扩展函数

定义扩展函数

扩展函数允许我们在不修改类的源代码的情况下,为现有类添加新的函数。

fun String.addExclamationMark() = this + "!"
val greeting = "Hello"
val newGreeting = greeting.addExclamationMark()
println(newGreeting)

这里我们为 String 类定义了一个扩展函数 addExclamationMark,它在字符串末尾添加一个感叹号。

扩展函数的作用域

扩展函数可以定义在顶层,也可以定义在类内部作为成员扩展函数。顶层扩展函数在整个项目中都可用,而成员扩展函数只能在包含它的类内部或其子类中使用。

class Outer {
    fun String.insideOuter() = this + " from inside Outer"
    fun callExtension() {
        val str = "Hello"
        println(str.insideOuter())
    }
}
val outer = Outer()
outer.callExtension()

这里 insideOuter 是定义在 Outer 类内部的成员扩展函数,只能在 Outer 类内部调用。

数据类

数据类的定义

数据类是一种特殊的类,主要用于保存数据。Kotlin 会自动为数据类生成一些有用的方法,如 equals()hashCode()toString()copy() 等。

data class User(val name: String, val age: Int)
val user1 = User("Bob", 28)
val user2 = User("Bob", 28)
println(user1 == user2)
println(user1)
val user3 = user1.copy(age = 29)
println(user3)

这里定义了一个 User 数据类,它有 nameage 两个属性。Kotlin 自动生成的 equals 方法比较两个 User 对象的属性值,toString 方法返回对象的字符串表示,copy 方法创建一个新的对象并可以修改部分属性值。

密封类

密封类的概念与定义

密封类用于表示受限的类继承结构,即一个密封类有一组有限的子类,并且这些子类都在与密封类相同的文件中声明。密封类使用 sealed 关键字定义。

sealed class Result
class Success : Result()
class Failure : Result()
fun handleResult(result: Result) {
    when (result) {
        is Success -> println("Operation successful")
        is Failure -> println("Operation failed")
    }
}
val successResult = Success()
val failureResult = Failure()
handleResult(successResult)
handleResult(failureResult)

这里 Result 是密封类,SuccessFailure 是它的子类。在 handleResult 函数中,when 语句可以在不使用 else 分支的情况下处理所有可能的 Result 子类,因为编译器知道 Result 只有这两个子类。

协程

协程基础

协程是 Kotlin 中用于异步编程的强大工具。它允许我们以更简洁的方式编写异步代码,避免回调地狱。首先,需要引入协程库依赖。在 Gradle 中,可以这样添加:

implementation 'org.jetbrains.kotlinx:kotlinx - coroutines - core:1.6.4'

然后,可以使用 launch 函数启动一个协程:

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        delay(1000)
        println("Hello from coroutine")
    }
    println("Hello from main")
}

这里 launch 函数创建并启动一个新的协程,delay 函数用于暂停协程执行指定的时间。runBlocking 函数用于阻塞主线程,直到所有子协程完成。

协程的并发与顺序执行

可以同时启动多个协程以实现并发执行,也可以按顺序执行协程。

fun main() = runBlocking {
    val job1 = launch {
        delay(1000)
        println("Job 1 completed")
    }
    val job2 = launch {
        delay(500)
        println("Job 2 completed")
    }
    job1.join()
    job2.join()
    println("All jobs completed")
}

这里启动了两个协程 job1job2,它们并发执行,join 方法用于等待协程完成,确保所有协程完成后再打印 “All jobs completed”。

如果要按顺序执行,可以这样写:

fun main() = runBlocking {
    launch {
        delay(1000)
        println("First task completed")
    }.join()
    launch {
        delay(500)
        println("Second task completed")
    }.join()
    println("All tasks completed")
}

协程的返回值

使用 async 函数可以启动一个有返回值的协程。

fun main() = runBlocking {
    val deferred = async {
        delay(1000)
        42
    }
    val result = deferred.await()
    println("The result is: $result")
}

这里 async 启动一个协程并返回一个 Deferred 对象,await 方法用于获取协程的返回值。

通过以上对 Kotlin 更多特性的介绍,你对 Kotlin 的理解应该更加深入了。无论是泛型带来的类型安全和代码复用,扩展函数的便捷性,数据类和密封类的特殊功能,还是协程在异步编程中的强大能力,都使得 Kotlin 成为一门功能丰富且高效的编程语言。在实际开发中,根据不同的需求和场景合理运用这些特性,可以编写出更加健壮、简洁和高效的代码。继续深入学习和实践,你将能够充分发挥 Kotlin 的潜力,开发出各种优秀的应用程序。例如在 Android 应用开发中,协程可以很好地处理网络请求、数据库操作等异步任务,提升用户体验;泛型和扩展函数可以优化代码结构,提高代码的可维护性和复用性。希望你在 Kotlin 的学习之旅中不断探索,收获更多编程乐趣和成果。