《Kotlin核心编程》上篇
基础语法
基础语法、类型推导、变量与函数声明、高阶函数、Lambda 表达式、流程控制以及字符串操作等主要知识点。这些内容是 Kotlin 编程的基础。
类型推导
Kotlin 具有强大的类型推导能力,在变量声明时如果初始值已经明确,类型可以省略不写。编译器会根据初始值自动推导变量类型。
val number = 10 // 编译器自动推导 number 为 Int 类型
val message = "Hello, Kotlin" // 自动推导为 String 类型
变量声明
val
声明的变量:一旦赋值不能再重新赋值,类似 Java 中的 final
变量,但更加简洁。var
声明的变量:可以在后续代码中重新赋值。
// val 声明的变量不可重新赋值
val PI: Double = 3.1415926
// PI = 3.14 // 这行代码会报错,因为 PI 是不可变的
// var 声明的变量可重新赋值
var count: Int = 0
count = 10
函数声明
Kotlin 中函数声明使用 fun
关键字,语法形式为 fun 函数名(参数列表): 返回类型 { 函数体 }
。如果函数体只有一行代码,可以使用表达式函数体的形式。
// 常规函数声明
fun add(a: Int, b: Int): Int {
return a + b
}
// 表达式函数体
fun multiply(a: Int, b: Int): Int = a * b
// 无返回值函数
fun printMessage(message: String): Unit {
println(message)
}
高阶函数
高阶函数是指可以接受函数作为参数,或者返回一个函数的函数。在 Kotlin 中,函数是一等公民,可以像其他数据类型一样进行传递和操作。
// 高阶函数,接受一个函数作为参数
fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
// 定义一个用于传递给高阶函数的普通函数
fun subtract(a: Int, b: Int): Int = a - b
fun main() {
val result1 = operate(5, 3, ::subtract)
println("Subtraction result: $result1")
// 使用 Lambda 表达式作为参数传递给高阶函数
val result2 = operate(5, 3) { x, y -> x + y }
println("Addition result: $result2")
}
Lambda表达式
Lambda 表达式是一种简洁的匿名函数,它可以作为表达式传递给高阶函数。其语法形式为 { 参数列表 -> 函数体 }
。
// 使用 Lambda 表达式作为函数参数
val numbers = listOf(1, 2, 3, 4, 5)
val sum = numbers.fold(0) { acc, num -> acc + num }
println("Sum of numbers: $sum")
// Lambda 表达式作为返回值
fun createAdder(): (Int) -> Int {
return { number -> number + 10 }
}
fun main() {
val adder = createAdder()
val result = adder(5)
println("Result of adding 10 to 5: $result")
}
表达式和流程控制
Kotlin 提供了常见的流程控制语句,如 if - else
、when
、for
、while
等。其中,if
表达式在 Kotlin 中可以作为表达式返回值,而不仅仅是语句。
// if - else 作为表达式
val max = if (5 > 3) 5 else 3
println("Max value: $max")
// when 表达式
val number = 3
val description = when (number) {
1 -> "One"
2 -> "Two"
else -> "Other"
}
println("Description of $number: $description")
// for 循环
for (i in 1..5) {
println(i)
}
// while 循环
var index = 0
while (index < 5) {
println(index)
index++
}
字符串操作
Kotlin 提供了丰富的字符串操作函数,如拼接、格式化、查找、替换等。字符串模板是 Kotlin 中一个非常实用的特性,可以在字符串中直接嵌入变量。
// 字符串拼接
val firstName = "John"
val lastName = "Doe"
val fullName = "$firstName $lastName"
println("Full name: $fullName")
// 字符串格式化
val age = 30
val message = "My name is $firstName and I'm $age years old."
println(message)
// 字符串查找
val text = "Hello, Kotlin"
val containsKotlin = text.contains("Kotlin")
println("Does the text contain 'Kotlin'? $containsKotlin")
// 字符串替换
val newText = text.replace("Kotlin", "World")
println("New text: $newText")
面向对象
Kotlin类和接口
Kotlin 类
-
定义与构造函数:Kotlin 中类的定义使用
class
关键字。类可以有主构造函数和次构造函数。主构造函数是类头的一部分,直接跟在类名之后。如果主构造函数没有任何注解或者可见性修饰符,可以省略constructor
关键字。
class Person(val name: String, var age: Int) {
// 主构造函数参数直接声明为属性
init {
println("A person named $name is created.")
}
// 次构造函数
constructor(name: String) : this(name, 0) {
println("A person named $name with age 0 is created.")
}
}
-
成员属性与方法:类可以包含属性和方法。属性有
val
(只读)和var
(读写)两种类型。方法的定义与普通函数类似,使用fun
关键字。
class Circle(val radius: Double) {
val area: Double
get() = Math.PI * radius * radius
fun circumference(): Double = 2 * Math.PI * radius
}
Kotlin 接口
-
定义与实现:接口使用
interface
关键字定义,接口可以包含抽象方法和非抽象方法(需有默认实现)。类通过:
符号实现接口。
interface Shape {
fun area(): Double
fun perimeter(): Double {
// 非抽象方法,有默认实现
return 0.0
}
}
class Rectangle(val width: Double, val height: Double) : Shape {
override fun area(): Double = width * height
override fun perimeter(): Double = 2 * (width + height)
}
Kotlin的修饰符
-
类修饰符:
-
public
:默认修饰符,类、属性和方法对所有调用者可见。 -
private
:类、属性或方法仅在声明它们的文件或类内部可见。 -
protected
:与private
类似,但在子类中也可见。 -
internal
:类、属性或方法在相同模块内可见
-
class Outer {
private val privateProperty = "Private"
protected val protectedProperty = "Protected"
internal val internalProperty = "Internal"
public val publicProperty = "Public"
private fun privateMethod() = "This is private"
protected fun protectedMethod() = "This is protected"
internal fun internalMethod() = "This is internal"
public fun publicMethod() = "This is public"
}
class Inner : Outer() {
fun accessProperties() {
// 可以访问 protectedProperty
println(protectedProperty)
// 不能访问 privateProperty
// println(privateProperty)
}
}
-
属性修饰符:除了上述可见性修饰符外,还有
lateinit
用于延迟初始化属性,const
用于声明编译时常量属性。小问题:lateinit var如何保证使用时一定被初始化?解决方案:1.在使用前进行非空判断,在使用lateinit var
变量之前,通过::variable.isInitialized
;2.在构造函数或init
块中初始化。3.在方法中严格按照顺序初始化。
class MyClass {
lateinit var lateinitVar: String
fun initLateinitVar() {
lateinitVar = "Initialized"
}
companion object {
const val CONSTANT_VALUE = 10
}
}
多继承问题
传统面向对象语言中类不能同时继承多个父类,这会导致菱形继承问题。Kotlin 通过接口来解决类似问题,一个类可以实现多个接口,接口可以有默认方法实现。虽然这不是严格意义上的多继承,但在一定程度上提供了类似功能,同时避免了菱形继承的复杂性。
interface Flyable {
fun fly() = println("I can fly")
}
interface Swimmable {
fun swim() = println("I can swim")
}
class Duck : Flyable, Swimmable
数据类
数据类使用 data
关键字定义,主要用于保存数据。Kotlin 会自动为数据类生成 equals()
、hashCode()
、toString()
、copy()
以及解构声明所需的函数。
data class User(val name: String, val age: Int)
fun main() {
val user1 = User("Alice", 25)
val user2 = User("Alice", 25)
println(user1 == user2) // 自动生成的 equals 方法比较内容,输出 true
println(user1) // 自动生成的 toString 方法,输出 User(name=Alice, age=25)
val (name, age) = user1 // 解构声明
println("$name is $age years old")
val user3 = user1.copy(age = 26) // 使用 copy 方法创建新对象
println(user3)
}
object
-
对象声明:使用
object
关键字可以声明一个单例对象,它在第一次被访问时初始化,且在整个应用程序生命周期内只有一个实例。
object Logger {
fun log(message: String) = println("Log: $message")
}
fun main() {
Logger.log("This is a log message")
}
-
伴生对象:在类内部使用
companion object
声明的对象,它可以包含类似于 Java 中静态成员的内容。伴生对象的成员可以通过类名直接访问。
class MathUtils {
companion object {
fun add(a: Int, b: Int): Int = a + b
}
}
fun main() {
val result = MathUtils.add(3, 5)
println("Result of addition: $result")
}
-
对象表达式:用于创建匿名对象,可实现接口或继承类。
val runnable = object : Runnable {
override fun run() {
println("Running in a new thread")
}
}
Thread(runnable).start()
代数数据类型和模式匹配
数据类型
-
基本数据类型
-
数值类型:Kotlin 有多种数值类型,如
Byte
(8 位)、Short
(16 位)、Int
(32 位)、Long
(64 位)、Float
(32 位浮点数)、Double
(64 位浮点数)。与 Java 不同,Kotlin 的数值类型是对象,有相应的成员函数。例如,123.toDouble()
可将Int
转换为Double
。 -
字符类型:
Char
用于表示单个字符,用单引号括起来,如'a'
。字符不能直接当作数字使用,但可以通过toInt()
等方法转换。 -
布尔类型:
Boolean
只有两个值true
和false
,用于逻辑判断。
-
- 可空类型
-
Kotlin 通过在类型后面加
?
来表示可空类型,如String?
。这有助于在编译时检测空指针异常。 -
访问可空类型变量的方法或属性时,需要进行空值检查。可以使用安全调用操作符
?.
,如nullableString?.length
,如果nullableString
为null
,表达式返回null
,而不会抛出空指针异常。
-
var nullableString: String? = "Hello"
nullableString = null
-
类型别名
-
可以使用
typealias
为现有类型定义别名,提高代码的可读性和维护性。例如:
-
typealias MyLong = Long
val myNumber: MyLong = 1234567890123456789L
模式匹配
-
when
表达式与模式匹配-
when
表达式在 Kotlin 中功能强大,不仅可以用于替代switch - case
,还支持模式匹配。例如:
-
fun describe(obj: Any) = when (obj) {
is String -> "It's a string with length ${obj.length}"
is Int -> "It's an integer"
else -> "Unknown type"
}
//这里的 is 关键字用于判断对象是否为某种类型,从而执行相应分支。
-
解构声明
-
解构声明允许将一个对象分解为多个变量。例如,对于一个包含两个元素的
Pair
: -
对于自定义类,如果类提供了
componentN()
函数(N
从 1 开始),也可以使用解构声明。例如:
-
val pair = Pair(1, "two")
val (first, second) = pair
println("$first, $second")
data class Point(val x: Int, val y: Int)
val point = Point(10, 20)
val (x, y) = point
println("$x, $y")
-
密封类与模式匹配
-
密封类的作用:1.表示受限的类继承结构,所有子类都在同一文件中定义;2.与
when
表达式配合实现安全的模式匹配;3.增强代码的可读性和可维护性:保证了 when 表达式覆盖了所有可能的 Result 子类。例如:
-
sealed class Result
class Success(val data: String) : Result()
class Failure(val error: String) : Result()
fun handleResult(result: Result) = when (result) {
is Success -> println("Success: ${result.data}")
is Failure -> println("Failure: ${result.error}")
}