招标网站怎么做,鞍山在百度做个网站多少钱,智能经济高峰论坛,php导航网站一、数据类型
1.1 变量声明与类型推导 变量声明 使用 val 声明不可变变量#xff08;相当于常量#xff09;#xff1b;使用 var 声明可变变量。 val a 10 // 类型自动推断为 Int#xff0c;不可变
var b: Double 5.0 // 显示声明为 Double#xff0c;可变变量…一、数据类型
1.1 变量声明与类型推导 变量声明 使用 val 声明不可变变量相当于常量使用 var 声明可变变量。 val a 10 // 类型自动推断为 Int不可变
var b: Double 5.0 // 显示声明为 Double可变变量类型推导 Kotlin 编译器会根据初始值自动推导出变量的类型无需手动指定
1.2 数值类型
Kotlin 提供了多种数值类型主要分为整数类型和浮点数类型 整数类型 Byte8 位范围 -128 到 127Short16 位范围 -32768 到 32767Int32 位默认类型范围约为 -21 亿到 21 亿Long64 位声明时需要在数值后加 L val num: Int 100
val bigNum 8000000000L // 需要使用 L 后缀浮点数类型 Float32 位声明时尾部加 f 或 FDouble64 位默认浮点数类型 val pi 3.14 // 自动推导为 Double
val floatPi 3.14f // 明确为 Float 类型1.3 字符类型与布尔类型 字符类型 使用 Char 表示单个字符常量用单引号括起来 val letter: Char A布尔类型 使用 Boolean 类型只有 true 和 false 两个值 val isVisible: Boolean true1.4 字符串 定义字符串 用双引号 括起可以直接嵌入变量或表达式字符串模板 val name Kotlin
val greeting Hello, $name!多行字符串 使用三个引号 定义原始字符串可以保留格式 val text 这是一个多行字符串可以保留原始格式
.trimIndent()String 模板 Kotlin 提供了字符串模板语法允许在字符串中嵌入变量和复杂表达式语法如下 变量插值 在字符串中直接使用 $变量名val name Kotlin
println(Hello, $name!)表达式插值 若需要插入的内容是复杂表达式需要用花括号 {}包裹如 ${表达式}val length name.length
println(字符串 \$name\ 的长度是 ${name.length})1.5.数组
数组创建 创建数组使用 arrayOf()对于基本数值类型可以使用专用函数如 intArrayOf()val intArray arrayOf(1, 2, 3)
val numbers intArrayOf(1, 2, 3, 4)数组访问
访问元素通过索引访问数组元素。
val arr arrayOf(1, 2, 3)
println(arr[0]) // 输出 1修改元素通过索引修改数组元素。
arr[0] 10
println(arr[0]) // 输出 10数组遍历
for
for (i in arr) {println(i)
}for (i in 0 until 3) { // 0, 1, 2println(i)
}for (i in 0..2) { // 0, 1, 2println(i)
}for(i in 3 downTo 1) { // 3, 2, 1println(i)
}forEach 使用高阶函数遍历数组。 arr.forEach { println(it) }nums.indices
fun twoSum(nums: IntArray, target: Int): IntArray {for (i in nums.indices) {for (j in i 1..nums.size) {if (nums[i] nums[j] target) {return intArrayOf(i, j)}}}
}常用操作
size
println(arr.size) // 输出 3contains检查数组是否包含某个元素。
println(arr.contains(2)) // 输出 trueslice获取数组的子集。
val subArr arr.slice(1..2)
println(subArr) // 输出 [2, 3]排序与搜索
sort对数组进行排序。
val sortedArr arr.sorted()
println(sortedArr) // 输出 [1, 2, 3]binarySearch在已排序的数组中进行二分查找。
val index sortedArr.binarySearch(2)
println(index) // 输出 1数组与集合的转换
toList将数组转换为列表。
val list arr.toList()
println(list) // 输出 [1, 2, 3]toSet将数组转换为集合。
val set arr.toSet()
println(set) // 输出 [1, 2, 3]1.6 类型转换 显式转换 Kotlin 不支持隐式类型转换必须调用转换函数进行转换 val intVal 10
val longVal intVal.toLong() // 转换为 Long 类型常用转换函数 包括 toByte()、toShort()、toInt()、toLong()、toFloat()、toDouble()、toChar() 等。
1.7 类型判断与智能转换 类型检查 使用 is 操作符判断对象类型 fun demo(x: Any) {if (x is String) {println(x.length) // 编译器自动将 x 当作 String 类型}
}强制类型转换 使用 as 操作符进行强制转换但转换失败会抛出异常。为避免异常可使用安全转换操作符 as?转换失败返回 null。*
1.8 可空类型与空安全 可空类型声明 在类型后加上 ? 表示该变量可以为 null var name: String? Kotlin
name null // 合法安全调用运算符 使用 ?. 来安全调用方法或属性避免空指针异常 val length name?.length // 若 name 为 null结果为 null非空断言 使用 !! 表示你确定该值不为 null但若实际为 null 则会抛出异常 println(name!!.length)Elvis 运算符 使用 ?: 提供默认值 val len name?.length ?: 0 // 若 name 为 null则 len 为 01.9 特殊类型
Any 类型 Kotlin 中所有非空类型的基类相当于 Java 的 Object 类型。Unit 类型 表示函数没有有意义的返回值类似 Java 中的 void但它是一个真正的类型其唯一值为 Unit。Nothing 类型 表示永远不会返回结果的类型常用于函数总是抛出异常的情况。
二、函数
2.1 基本函数定义 定义方式 定义函数格式如下 fun 函数名(参数1: 类型, 参数2: 类型, …): 返回类型 {// 函数体return 返回值
}如果函数体只有一行表达式可以省略大括号、return 以及返回类型编译器可自动推断 fun add(a: Int, b: Int) a b2.2 参数相关
默认参数与具名参数 默认参数 Kotlin 允许为函数参数设定默认值从而在调用时可以省略对应的参数 fun greet(name: String Kotlin): String {return Hello, $name!
}
// 调用
println(greet()) // 输出 Hello, Kotlin!
println(greet(name 张三)) // 使用具名参数具名参数 fun greet(name: String, age: Int) {println($name is $age years old.)
}// 调用
greet(name Alice, age 30) // 常规顺序
greet(age 30, name Alice) // 打乱顺序所有位置参数必须位于第一个具名参数之前 fun example(a: Int, b: Int, c: Int) { }example(1, c 3, b 2) // 合法
// example(a 1, 2, 3) // 编译错误位置参数在具名参数后可变参数 (vararg) 可变参数 使用 vararg修饰参数表示传入的参数个数可变内部作为数组处理 fun T asList(vararg items: T): ListT {val result ArrayListT()for (item in items) {result.add(item)}return result
}
// 调用
val list asList(1, 2, 3, 4)2.3 单表达式函数
当函数只有一行表达式时可使用单表达式函数写法
fun square(x: Int) x * x返回值类型可由编译器推断
2.4 局部函数
Kotlin 允许在一个函数内部定义其他函数这叫做局部函数nested function它可以访问外部函数的局部变量
fun performOperation(numbers: ListInt): Int {var sum 0fun add(value: Int) {sum value}for (num in numbers) {add(num)}return sum
}2.5. Lambda 表达式 使用大括号包裹参数和函数体参数与函数体之间用箭头 -分隔 val multiply { x: Int, y: Int - x * y }
println(multiply(3, 4)) // 输出 12隐式参数 it 如果 lambda 只有一个参数则可以使用隐式参数 itval printMessage: (String) - Unit { println(it) }
printMessage(Hello Lambda)2.6 匿名函数 没有名称的函数可直接赋值给变量、作为参数传递或从其他函数返回。 即时性无需预先声明即用即定义 隐式返回自动返回函数体最后一行表达式的结果无需retuen关键字 val greeting { Hello, Kotlin! } // 隐式返回字符串
println(greeting()) // 输出Hello, Kotlin!参数处理 类型声明匿名函数类型由参数和返回值决定例如 (String, Int) - String参数规则 多参数需明确声明参数名和类型 val add { a: Int, b: Int - a b }单参数可使用it简化 val square: (Int) - Int { it * it }
println(square(5))类型推断
若匿名函数赋值给变量时已明确类型可省略参数类型声明但需确保编译器能推断出类型
val multiply { a: Int, b: Int - a * b } // 显式声明参数类型
val multiplyInferred: (Int, Int) - Int { a, b - a * b } // 类型推断显式返回类型
与 Lambda 不同匿名函数支持显式声明返回类型适用于需要明确返回值的场景
val divide fun(a: Int, b: Int): Double {if (b 0) return 0.0 // 显式返回return a.toDouble() / b
}2.7 高阶函数
高阶函数是将函数作为参数或返回值的函数例如
// 函数作为参数
fun operate(a: Int, b: Int, op: (Int, Int) - Int): Int {return op(a, b)
}// 匿名函数作为最后一个参数省略了最外层括号
val sum operate(4, 5) { x, y - x y }
println(sum) // 输出 9
// 函数作为返回值
fun makeMultiplier(factor: Int): (Int) - Int {return { number - number * factor }
}
// timesThree是返回的函数
val timesThree makeMultiplier(3)
// 调用返回的函数
println(timesThree(10)) // 输出 302.8 内联函数 类似于C语言宏定义的替换 inline修饰高阶函数可以在编译时将函数体内联从而减少函数调用开销。 inline fun runOperation(operation: () - Unit) {operation()
}2.9 函数类型与函数引用 函数类型 Kotlin 中函数本身是“一等公民”其类型写作 (参数类型1, 参数类型2, …) - 返回类型val func: (Int, Int) - Int ::add // 使用函数引用引用已有函数
println(func(3, 7))函数引用 ::符号引用函数 fun add(a: Int, b: Int) a b
val addRef ::add
println(addRef(5, 10)) // 输出 152.10 闭包 闭包是能够读取其他函数内部变量的函数即使外部函数已执行完毕闭包仍能访问其作用域中的变量 // 匿名函数持有外部函数 doRepeat 的 count 变量形成闭包
fun doRepeat(): () - Unit {var count 1return fun() { println(count) }
}核心特性 状态持久化 闭包会延长外部函数变量的生命周期使其常驻内存。例如多次调用 doRepeat() 生成的闭包会独立维护各自的 count 变量 val doRepeat1 doRepeat() // count 初始化为1
doRepeat1() // 输出1 → count变为2
doRepeat1() // 输出2 → count变为3独立性 不同闭包实例持有独立的状态。例如doRepeat1 和 doRepeat2 的 count 互不影响 val doRepeat1 doRepeat()
val doRepeat2 doRepeat()
doRepeat1() // 输出1
doRepeat2() // 输出1独立计数简化代码与避免全局污染 闭包可替代全局变量例如实现斐波那契数列的状态缓存 fun fib(): () - Long {var prev 0Lvar current 1Lreturn fun(): Long {val result currentcurrent prevprev resultreturn result}
}2.11 反引号中的函数名
Kotlin可以使用空格和特殊字符对函数命名不过函数名要用一对反引号括起来。
当 Java 方法名与 Kotlin 的保留字如 is、in、object同名时Kotlin 需用反引号转义调用。 例如Java 方法 is() 在 Kotlin 中需写为 foo.is(bar)可用于测试用例等场景例如
fun test user login with invalid password() { ... }三、null安全与异常
3.1 null安全机制
非空类型与可空类型 非空类型在Kotlin中默认变量不允许为null 声明时必须初始化且不能赋值为null编译器会强制确保变量始终持有有效值 val name: String Kotlin可空类型如果允许变量为null必须在类型后加问号? 直接调用其方法或属性会触发编译错误需通过安全操作符处理 val name: String? null?. ?: 操作符和let 安全调用操作符 ?. 当对象可能为null时使用 ?.访问其属性或方法可以避免NullPointerException val length name?.length // 如果name为nulllength将为null空合并操作符 ?: 用于在对象为null时提供默认值 val length name?.length ?: 0 // 如果name为null则返回0let函数 在 let 的函数体内调用者对象使用 it 作为默认名称let 的返回值是函数块的最后一行或指定的返回值 val name: String? Kotlin
name?.let {println(Name is $it)
}
// 如果 name 为 null则不会执行 let 块内的代码val length yuxuan.let {it.length
}
println(length)3.2 异常处理机制
try-catch
try-catch-finally结构 与Java类似 try-catch-finallytry {// 可能抛出异常的代码
} catch (e: Exception) {// 异常处理
} finally {// 无论是否异常都会执行的代码
}var num: Int? null // 声明一个可为空的Int类型变量
try {// 使用非空断言运算符(!!)强制解包可能为null的变量println(num!!.plus(11))
} catch (e: Exception) {println(e)
}!! 非空断言操作符 !! 强制将一个可空类型转为非空类型如果对象为null会抛出NullPointerException val length name!!.length // 若name为null则会抛出异常先决条件函数
函数描述checkNotNull如果参数为null则抛出IllegalStateException异常否则返回非null值require如果参数为false则抛出IllegalArgumentException异常requireNotNull如果参数为null则抛出IllegalStateException异常否则返回非null值error如果参数为null则抛出IllegalStateException异常并输出错误信息否则返回非null值assert如果参数为false则抛出AssertionError异常并打上断言编译器标记
require()函数 作用用于检查函数参数的先决条件。如果条件不满足会抛出IllegalArgumentException。 示例 fun setAge(age: Int) {require(age 0) { 年龄必须为正数 }// 其他逻辑
}assert()函数 作用用于调试时的断言检查。如果条件为false则抛出AssertionError。 注意在生产环境中断言通常会被禁用仅在开发或测试阶段使用。 fun test(value: Int) {assert(value 0) { 值必须大于0 }// 测试代码
}error()函数 作用用于直接抛出异常常用于未实现的代码块或错误状态。 fun notImplemented() {error(尚未实现该功能)
}四、字符串
4.1 定义
普通字符串使用双引号括起来可以包含转义字符如换行符 \n。 val str1 Hello\nWorldprintln(str1)// Hello// World原始字符串使用三重引号 括起来支持多行且不会转义特殊字符。 val str2 第1行第2行.trimIndent()println(str2)// 输出// 第1行// 第2行4.2 字符串模板
Kotlin 提供字符串模板功能可以在字符串中嵌入变量或表达式。
val name Kotlin
val age 5
println(语言$name年龄$age 岁)
println(2 3 ${2 3})
// 输出
// 语言Kotlin年龄5 岁
// 2 3 54.3 常用操作
查找indexOf() 返回子字符串的起始位置未找到则返回 -1。 val text Kotlin 编程val index text.indexOf(编程)println(index) // 输出7替换replace() 用于替换指定的子字符串。
// fun replace(oldValue: String, newValue: String, ignoreCase: Boolean false)
val text Hello Kotlin
val newText text.replace(Kotlin, World)
println(newText) // 输出Hello World// fun replace(regex: Regex, replacement: String): String
val text 请联系我123-456-7890 或 987-654-3210。
val regex Regex(\\d{3}-\\d{3}-\\d{4})
val result text.replace(regex, [号码已隐藏])
println(result)
// 请联系我[号码已隐藏] 或 [号码已隐藏]。分割split() 根据指定分隔符将字符串分割为List集合
val text apple,banana,cherry
val fruits text.split(,)
println(fruits) // 输出[apple, banana, cherry]// 解构语法特性
val text apple,banana,cherry
val (fruit1, fruit2, fruit3) text.split(,)
println(fruit1) // 输出apple
println(fruit2) // 输出banana
println(fruit3) // 输出cherry
截取substring() 截取指定范围内的子字符串。
val text Kotlin 编程
val subText text.substring(0, 6)
println(subText) // 输出Kotlinval text apple,banana,cherry
val subText text.substring(6..11)
println(subText) // 输出banana遍历可以使用索引或直接遍历字符。
val text Kotlin
for (char in text) {println(char)
}text.forEach { print($it ) }4.4 空安全
提供多种方法来判断字符串的空值或空白状态
isNullOrEmpty()为空指针或长度为 0 时返回 true。isNullOrBlank()为空指针、长度为 0 或全为空格时返回 true。isEmpty()长度为 0 时返回 true。isNotEmpty()长度大于 0 时返回 true。isBlank()长度为 0 或全为空格时返回 true。isNotBlank()长度大于 0 且不全为空格时返回 true。
val str: String?
println(str.isNullOrEmpty()) // 输出false
println(str.isNullOrBlank()) // 输出true4.5 字符串比较 检查两个字符串中的字符是否匹配 检查两个变量是否指向内存堆上同一对象 1. 使用 运算符进行内容比较
在Kotlin中运算符用于比较两个对象的内容是否相等。当用于字符串时它会比较字符串的字符序列是否相同。需要注意的是在Kotlin中被重载实际调用的是equals()方法。
示例
val str1 Kotlin
val str2 Kotlin
val str3 kotlinprintln(str1 str2) // 输出true
println(str1 str3) // 输出false2. 使用 运算符进行引用比较
运算符用于比较两个变量是否引用同一个对象实例即比较它们的引用是否相同。这与运算符的内容比较不同
val str1 Kotlin
val str2 Kotlin
val str3 String(Kotlin.toCharArray())println(str1 str2) // 输出true因为字符串常量池的原因
println(str1 str3) // 输出false因为str3是通过构造函数创建的新的String对象3. 使用 equals() 方法进行比较
equals()方法用于比较两个对象的内容是否相等。与运算符类似但equals()方法提供了更多的控制例如可以指定是否忽略大小写。
val str1 Kotlin
val str2 kotlinprintln(str1.equals(str2)) // 输出false默认区分大小写
println(str1.equals(str2, ignoreCase true)) // 输出true忽略大小写五、标准库函数
5.1 作用域函数 可以让你在一个代码块内访问某个对象从而减少重复引用对象名称并能链式调用。 函数对象引用方式返回值是否扩展函数letitlambda 表达式的结果是runthislambda 表达式的结果是withthislambda 表达式的结果否作为参数传入applythis调用对象本身是alsoit调用对象本身是
let 特点 将调用对象作为参数传入 lambda默认名称为 it。返回 lambda 表达式最后一行的值。常用于空值检查结合 ?. 使用或局部变量转换。 示例 val str: String? Hello
str?.let {println(字符串不为空$it)// 返回最后一行表达式的结果it.length
}run 特点 有两种用法 直接调用 run { … }不需要对象此时 lambda 无接收者作为扩展函数调用即对象.run { … }此时 lambda 的接收者为该对象可直接使用 this。 返回 lambda 最后一行的值。 示例 // 直接调用
val result run {println(直接调用 run)100
}
println(result)
// 直接调用 run
// 100// 对象调用 run
val person Person(Alice, 30)
val info person.run {姓名$name, 年龄$age
}
println(info)with 特点 不是扩展函数而是一个普通函数需要将对象作为第一个参数传入。在 lambda 内部可以直接使用 this 调用对象方法。返回 lambda 表达式最后一行的结果。 示例 val sb StringBuilder(Kotlin:)
val resultString with(sb) {append(标准库函数笔记)toString()
}
println(resultString)apply 特点 是扩展函数其 lambda 内部的接收者为调用对象this 可直接访问对象成员。返回调用对象本身因此常用于对象的初始化配置。 示例 val person Person(Bob, 25).apply {// 在这里配置 person 对象println(初始化 person 对象$name, $age)
}val file: File File(111).apply {setReadable(true)setWritable(true)setExecutable(false)
}also 特点 与 apply 类似都是扩展函数且返回调用对象本身不同之处在于 lambda 中的对象通过 it 传入而不是 this。常用于在链式调用中做额外操作例如日志记录、调试。 示例 val numbers mutableListOf(1, 2, 3)
numbers.also {println(操作前的列表$it)
}.add(4)
println(操作后的列表$numbers)5.2 其他常用标准库函数
takeIf 与 takeUnless takeIf 如果给定的谓词为 true则返回对象本身否则返回 null。常用于将条件判断嵌入调用链中。 示例 val number 42
val evenNumber number.takeIf { it % 2 0 }
println(evenNumber) // 输出 42val oddNumber number.takeIf { it % 2 ! 0 }
println(oddNumber) // 输出 nulltakeUnless 与 takeIf 相反条件为 false时返回对象本身为 true时返回 null。 示例 val strValue Kotlin
val result strValue.takeUnless { it.isEmpty() }
println(result) // 输出 Kotlinrepeat 特点 用于重复执行一个操作指定次数常见于打印、计数等场景。 示例 repeat(5) {println(Hello, Kotlin!)
}5.3 应用场景与选择
对非空对象进行操作时使用 ?.let { } 能避免显式的空检查。对象的初始化配置apply 是首选因为它返回对象本身且代码风格简洁。在不改变对象的情况下执行额外操作例如日志记录可以使用 also。如果需要对一个代码块求值并返回结果但又不关心对象本身可使用 run 或 with后者适用于需要多次调用同一对象的场景。
六、集合
6.1 集合分类
Kotlin 中的集合主要分为两大类
只读集合提供读取操作不能修改内部元素如 ListTSetTMapK, V 可变集合在只读集合基础上增加写操作添加、删除、更新如 MutableListTMutableSetTMutableMapK, V
6.2 List 集合
不可变 List 创建 listOf()函数创建不可变 List例如 val list listOf(1, 3, 5, 7)访问元素 索引访问list[0] 或 list.get(0)获取首尾list.first()、list.last()安全访问list.elementAtOrNull(index)越界时返回 null 取子集 subList(fromIndex, toIndex)左闭右开区间 println(list.subList(0, 2)) // 输出[1, 3]可变 List 创建 mutableListOf()val mList mutableListOf(one, two, three)常用操作 添加mList.add(four)删除mList.removeAt(0) 或 mList.remove(two)更新mList[0] newOne随机打乱mList.shuffle()过滤删除mList.removeAll { it.length 3 }
遍历
直接 for 循环遍历元素 适用于只需要处理集合中每个元素的情况
val list listOf(a, b, c)
for (item in list) {println(item)
}通过索引遍历 使用 indices 获取索引范围再通过索引访问对应的值
for (i in list.indices) {println(index $i, value ${list[i]})
}使用 withIndex() 进行遍历 这种方式既能同时获得元素的索引和值又让代码看起来非常清晰
for ((index, value) in list.withIndex()) {println(index $index, value $value)
}使用高阶函数 forEach()
list.forEach { item -println(item)
}使用函数 forEachIndexed 允许在遍历集合的同时获取每个元素的索引和值
val fruits listOf(Apple, Banana, Cherry)
fruits.forEachIndexed { index, fruit -println(索引$index元素$fruit)
}6.3 Set 集合
基本特点
唯一性Set 中的每个元素都是唯一的。创建 不可变 SetsetOf(one, two, three)可变 SetmutableSetOf(one, two, three) 内部实现通常默认采用 LinkedHashSet可保持插入顺序。
常用操作 遍历使用 forEach 或 forEachIndexed 集合运算 并集set1 union set2交集set1 intersect set2差集set1 subtract set2 list 转 set val list listOf(apple, banana, apple, orange)
// 可变或不可变
val set list.toSet()
val mutableSet list.toMutableSet()val list listOf(apple, banana, apple, orange)
val distinctList list.distinct()
println(distinctList) // [apple, banana, orange]data class Person(val name: String, val age: Int)val people listOf(Person(Alice, 20),Person(Bob, 25),Person(Alice, 30)
)// 根据 name 去重保留第一个出现的
val distinctPeople people.distinctBy { it.name }
println(distinctPeople)
// 输出[Person(nameAlice, age20), Person(nameBob, age25)]6.4 Map 集合
基本特点
存储键值对键Key必须唯一值Value可以重复。创建 不可变 MapmapOf(key1 to 1, key2 to 2, key3 to 3)可变 MapmutableMapOf(key1 to 1, key2 to 2)
常用操作 访问键和值map.keys、map.values、map.entries getOrPut 函数用于在映射中获取指定键的值如果键不存在则使用提供的默认值通过 lambda 表达式计算插入该键并返回该值。 通过键取值map[key1] 过滤操作 filter { (key, value) - … }filterKeys { … }filterValues { … } Map 转换利用 associateWith {}、associateBy {} 或 associate {} 将 List 转换成 Map
遍历
使用 for 循环
val map mapOf(apple to 苹果, banana to 香蕉)
for ((key, value) in map) {println(key $key, value $value)
}使用 forEach 函数
val map mapOf(apple to 苹果, banana to 香蕉)
map.forEach { (key, value) -println(key $key, value $value)
}遍历 Map 的 keys 或 values 键或值可以分别遍历
// 遍历所有键
for (key in map.keys) {println(key $key)
}// 遍历所有值
for (value in map.values) {println(value $value)
}6.5 排序与聚合
排序 sorted()、sortedDescending()自然顺序升序/降序排序。sortedBy { }、sortedWith()根据指定规则排序。reversed()、asReversed()、shuffled()倒序、反向视图、随机排列。 聚合 数量统计count()最大最小maxOrNull()、minOrNull()平均、求和average()、sum()累加reduce()、fold()以及它们的右侧版本
七、定义类
7.1 构造函数
主构造函数
Kotlin 的类可以有一个主构造函数和一个或多个次构造函数。主构造函数是类头的一部分位于类名之后
class Person constructor(firstName: String) {
}如果主构造函数没有任何注解或可见性修饰符可以省略 constructor 关键字
class Person(firstName: String) {
}示例
// Kotlin中的主构造函数如果不使用var或val修饰则这些参数仅作为构造函数的形参传入
// 而不会自动成为类的属性。
class Person constructor(_name: String, _age: Int, _height: String
) {// 定义name属性并用传入的_name初始化// Kotlin 中的属性有默认的 getter 和 setter但也可以自定义// 自定义getter: 每次获取name时返回首字母大写后的字符串// 自定义setter: 设置name时先去掉前后空格再赋值var name _nameget() field.capitalize()set(value) {field value.trim()}// 定义age属性直接使用传入的_age初始化var age _age// 定义height属性直接使用传入的_height初始化var height _heightoverride fun toString(): String {return Person(age$age, height$height, name$name)}
}fun main() {// 创建一个Person对象传入对应的参数var p1 Person(yuxuan, 20, 175)println(p1.toString())
}次构造函数
次构造函数使用关键字 constructor 来声明与主构造函数不同的是它需要用委托调用主构造函数或者其他次构造函数。
每个次构造函数必须通过委托调用其他构造函数来最终调用主构造函数如果存在主构造函数这确保了所有属性都能被正确初始化。
例如
class Person(val name: String, val age: Int) {// 次构造函数提供只传入 name 的构造方式constructor(name: String) : this(name, 0) {// 次构造函数体中可以进行额外的初始化操作println(使用次构造函数仅传入 name 参数)}
}fun main() {val person Person(Bob) // 使用次构造函数
}次构造函数仅需要传入 name然后通过 : this(name, 0) 委托调用主构造函数并为 age 提供默认值 0。
7.2 初始化顺序 主构造函数参数求值 → 属性初始化与 init 块执行 → 次构造函数体执行 1. 主构造函数的参数求值
当你创建一个对象时首先会对传递给主构造函数的参数进行求值。
2. 属性初始化与 init 块
属性初始化接着会按它们在类中声明的顺序执行属性初始化语句。例如init 块随后会按顺序执行所有的 init 块中的代码。这两个步骤一起保证了所有属性在构造过程中被正确地初始化。 无论你使用主构造函数还是次构造函数这一步总是会在构造函数体执行之前完成。 3. 次构造函数的调用
如果你是通过次构造函数来创建对象次构造函数首先必须委托调用主构造函数或者间接调用其他次构造函数这样就会触发前面提到的属性初始化和 init 块的执行。当主构造函数、属性初始化和所有 init 块执行完毕后才会执行次构造函数体中的代码。
举例
class Person(val name: String) {var age: Int 0// 属性初始化var info: String 初始化属性// 第一个 init 块init {println(init 块1: name $name)}// 第二个 init 块init {println(init 块2: info $info)}// 次构造函数委托调用主构造函数constructor(name: String, age: Int) : this(name) {this.age ageprintln(次构造函数体: age $age)}
}fun main() {// 调用次构造函数创建对象val p Person(Alice, 25)
}顺序如下
求值主构造函数参数Alice 被传入。属性初始化age 被初始化为 0info 被初始化为 初始化属性。init 块执行按顺序打印 init 块1: name Aliceinit 块2: info 初始化属性 次构造函数体执行age 被更新为 25并打印 次构造函数体: age 25
7.3 类的继承
Kotlin 中的类默认是 final 的不能被继承。如果需要一个类可以被继承需要使用 open 关键字修饰
open class Base(p: Int)class Derived(p: Int) : Base(p)7.4 抽象类
抽象类使用 abstract 关键字修饰不能被实例化可以包含抽象成员
abstract class Polygon {abstract fun draw()
}7.5 类的可见性修饰符
Kotlin 提供了四个可见性修饰符private、protected、internal 和 public。默认情况下所有类都是 public 的
private仅在同一个文件中可见。protected同一个文件中或子类中可见。internal同一个模块中可见。public在任何地方可见。
7.6 延迟初始化
lazy 委托属性 适用场景通常用于 val 类型的属性并且初始化操作可能较为耗时或者不一定需要立即执行。 特点 延迟到第一次使用时执行初始化代码。默认线程安全可以通过传递参数修改线程安全策略。返回值会被缓存下次访问时直接返回之前计算的值。 示例代码 val lazyValue: String by lazy {println(初始化 lazyValue)Hello, Lazy!
}fun main() {println(调用 lazyValue 前)println(lazyValue) // 第一次调用执行初始化代码并打印 初始化 lazyValueprintln(lazyValue) // 第二次调用直接返回缓存值不再执行 lambda
}输出结果 调用 lazyValue 前
初始化 lazyValue
Hello, Lazy!
Hello, Lazy!这里lazyValue 的初始化代码只会在第一次访问时执行一次之后直接返回缓存的值。
lateinit 修饰符 适用场景适用于 var 类型的属性通常用于那些不能在声明时就初始化的情况例如依赖于外部注入或后续赋值但必须保证在使用前一定被初始化。 特点 不能用于基本数据类型如 Int、Double 等。编译器不会强制在声明时初始化但在访问时如果没有初始化会抛出 UninitializedPropertyAccessException。 示例代码 class Example {lateinit var data: Stringfun initData() {data Hello, Lateinit!}fun printData() {if (::data.isInitialized) { // 检查是否已初始化println(data)} else {println(data 还未初始化)}}
}fun main() {val example Example()example.printData() // 输出 data 还未初始化example.initData()example.printData() // 输出 Hello, Lateinit!
}在这个例子中data 属性使用 lateinit 修饰表示稍后会被初始化并且在使用前可以通过 ::data.isInitialized 检查其是否已赋值。