目录
[2.1 输入](#2.1 输入)
[1.1 整数类型](#1.1 整数类型)
[1.2 浮点类型](#1.2 浮点类型)
[1.3 数字字面常量](#1.3 数字字面常量)
[1.4 装箱与缓存](#1.4 装箱与缓存)
[1.4.1 JVM是怎么存储数字的?](#1.4.1 JVM是怎么存储数字的?)
[1.4.2 使用可空类型的时候会触发装箱操作:](#1.4.2 使用可空类型的时候会触发装箱操作:)
[1.4.3 JVM 对 -128 到 127 的小数字做了特殊优化:](#1.4.3 JVM 对 -128 到 127 的小数字做了特殊优化:)
[1.5 显式数字转换](#1.5 显式数字转换)
[1.6 数字运算](#1.6 数字运算)
[1.7 整数除法](#1.7 整数除法)
[3.1 特殊字符](#3.1 特殊字符)
[4.1 基础特性](#4.1 基础特性)
[4.2 常用操作](#4.2 常用操作)
[4.3 扩展函数增强](#4.3 扩展函数增强)
[4.4 性能优化技巧](#4.4 性能优化技巧)
[4.5 与Java互操作](#4.5 与Java互操作)
[1.条件语句(if 与 when)](#1.条件语句(if 与 when))
[2.1 遍历范围(Range)](#2.1 遍历范围(Range))
[2.2 遍历数组](#2.2 遍历数组)
[2.3 遍历集合(List/Set)](#2.3 遍历集合(List/Set))
[2.4 遍历 Map](#2.4 遍历 Map)
[2.5 遍历字符串字符](#2.5 遍历字符串字符)
[2.6 自定义迭代器](#2.6 自定义迭代器)
[2.7 控制流操作](#2.7 控制流操作)
[2.8 标签控制](#2.8 标签控制)
[2.9 与 Java 对比](#2.9 与 Java 对比)
[2.1 使用案例](#2.1 使用案例)
[3.1 语法](#3.1 语法)
[3.2 使用案例](#3.2 使用案例)
[3.1 安全处理空值](#3.1 安全处理空值)
一、基本概念
1.包的定义与导入
包的声明应处于源文件顶部:
Kotlin
package my.demo //包名
import kotlin.text.* //引用某个包
2.程序入口点
Kotlin 应用程序的入口点是 main 函数:
Kotlin
fun main() {
println("Hello world!")
}
main 的另一种形式接受可变数量的 String 参数,当程序需要处理命令行参数时可以使用这个形式,感兴趣可自行扩展(如读取文件名、配置选项等):
Kotlin
fun main(args: Array<String>) {
println(args.contentToString())
}
2.1 输入
Kotlin
// Prints a message to request input
println("请输入: ")
// 这里使用了一个不可变的youword参数,读取并存储用户输入,
val yourWord = readln()
// 显示你输入的信息
print("你输入了: ")
print(yourWord)
3.变量
kotlin具有强大的类型推断,我们可以让编译器通过推断变量的类型来完成工作。
Kotlin
//声明一个可变变量并初始化它
var a: String = "initial"
println(a)
//声明一个不可变变量并初始化它。
val b: Int = 1
//声明不可变变量并在不指定类型的情况下对其进行初始化。编译器推断Int类型。
val c = 3
变量必须初始化后才可以使用,否则会报错,只要在第一次使用它之前做初始化就可以
Kotlin
//声明一个没有初始化的变量。
var e: Int
//尝试使用该变量会导致编译器错误:变量'e'必须初始化。
println(e)
//初始化后再用
e = 666
二、基本类型
Kotlin 中使用的类型:数字、布尔、字符、字符串、数组。
1.数字
1.1 整数类型
Kotlin 提供了一组表示数字的内置类型。 对于整数,有四种不同大小与取值范围的类型:
|-------|---------|----------------------------|---------------------------|
| 类型 | 大小(比特数) | 最小值 | 最大值 |
| Byte | 8 | -128 | 127 |
| Short | 16 | -32768 | 32767 |
| Int | 32 | -2,147,483,648 | 2,147,483,647 |
| Long | 64 | -9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 |
当初始化一个没有显式指定类型的变量时,编译器会自动推断为自 Int 起****足以表示该值的最小类型。 如果不超过 Int 的取值范围,那么类型是 Int。 如果超出了该范围,那么类型是 Long。 如需显式指定 Long 值,请给该值追加后缀 L。 显式指定类型会触发编译器检测该值不会超出指定类型的取值范围。
Kotlin
val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1
1.2 浮点类型
对于实数,Kotlin 提供了浮点类型 Float单精度 与 Double双精度两种类型
|--------|---------|--------|------------------|----------------------------|
| 类型 | 大小(比特数) | 10进制位数 | 最小值 | 最大值 |
| Float | 32 | 6-7 | 3.4028235 × 10³⁸ | 1.7976931348623157 × 10³⁰⁸ |
| Double | 64 | 15-16 | 1.4 × 10⁻⁴⁵ | 4.9 × 10⁻³²⁴ |
对于以小数初始化的变量,编译器会推断为 Double 类型:
Kotlin
val pi = 3.14 // Double
val one: Double = 1 //Double
val oneDouble = 1.0 // Double
如需将一个值显式指定为 Float 类型,请添加 f 或 F 后缀。 如果以这种方式提供的值包含多于 7 位十进制数,那么会将其舍入:
Kotlin
val e = 2.7182818284 // Double
val eFloat = 2.7182818284f // Float,实际值为 2.7182817
与一些其他语言不同,Kotlin 中的数字没有隐式拓宽转换。 例如,具有 Double 参数的函数只能对 Double 值调用,而不能对 Float、 Int 或者其他数字值调用:
Kotlin
fun main() {
//sampleStart
fun printDouble(x: Double) { print(x) }
val x = 1.0
val xInt = 1
val xFloat = 1.0f
printDouble(x)//参数类型匹配
printDouble(xInt) // 参数类型不匹配
printDouble(xFloat) // 参数类型不匹配
//如果需要让xInt或者xFloat使用这个函数,需要先进行显式类型转换
xInt.toDouble()
}
1.3 数字字面常量
数值常量(直接在代码中表示固定值的符号)字面值有以下几种:
- 十进制:123
- Long 类型,以大写 L 结尾:123L
- 十六进制:0x0F
- 二进制:0b00001011
Kotlin 不支持八进制。
Kotlin 同样支持浮点数的常规表示方法:
- Doubles (default when the fractional part does not end with a letter): 123.5, 123.5e10
- Float,以字母 f 或 F 结尾:123.5f
你可以使用下划线使数字常量更易读:
Kotlin
val oneMillion = 1_000_000 //实际值仍然是 1000000(一百万),下划线 _ 作为视觉分隔符,使数字更易读.在编译阶段会被完全忽略,不影响实际值
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010
val bigFractional = 1_234_567.7182818284
1.4 装箱与缓存
1.4.1 JVM是怎么存储数字的?
- 基本类型:int、double 等(直接存数值,高效)
- 装箱类型:Integer、Double 等(存对象,用于特殊场景)
1.4.2 使用可空类型的时候会触发装箱操作:
- Int? 中的问号(?)表示可空类型(Nullable Type),这是 Kotlin 类型系统中最重要的特性之一
- Kotlin 通过问号语法,强制开发者显式处理 null 的情况,避免 NullPointerException
Kotlin
val basic: Int = 10
// 基本类型(高效) 不可为 null 的整数(等价于 Java 的 int 基本类型)
val boxed: Int? = 10
// 装箱类型(对象) 可以为 null 的整数(等价于 Java 的 Integer 包装类,但更安全)
1.4.3 JVM 对 -128 到 127 的小数字做了特殊优化:
- 这些数字装箱后会共用同一个缓存对象
- 超出范围的数字每次装箱都会创建新对象
Kotlin
//假如是1个127以内的数,jvm会对其进行缓存,哪怕装箱以后,也都会指向同一个对象
val b: Int = 100
val boxedB: Int? = b // 装箱
val anotherBoxedB: Int? = b // 再次装箱
println(boxedB === anotherBoxedB) // 因为100没超出缓存范围,两个引用指向同一个对象 输出:true
println(boxedB == anotherBoxedB) // == 比较的是数值内容,两者都是100 输出:true
//但如果b是的范围不属于 -128 到 127,那么就不再是同一个对象
val b: Int = 10000
println(boxedB === anotherBoxedB) // 因为10000超出缓存范围,每次装箱都创建新对象 输出:false
注意:这里只是借助?可空类型以及===来说明装箱后的缓存现象:
- 除非必要,不要用 Int?,一定是优先使用基本类型
- 始终使用 == 比较数字,而不是使用===比较内存地址,因为这似乎没啥意义
1.5 显式数字转换
(1) 较小的类型不会隐式地转换为较大的类型,反之亦然。例如,将Byte类型的值赋给Int变量需要显式转换
Kotlin
fun main() {
val byte: Byte = 1
val intAssignedByte: Int = byte //会报错
val intConvertedByte: Int = byte.toInt()//需要显示转换
println(intConvertedByte)
}
所有数字类型都支持转换为其他类型,只需要调用数字下的 toxxx函数方法:toByte()、toShort()、toInt()、toLong()、toFloat()、toDouble()。
(2) 也有很多不需要显式类型转换的情况,因为程序会根据上下文推断类型, 而且算术运算符也会有重载自动处理类型转换。例如:
Kotlin
fun main() {
// 场景1:根据赋值自动推断
val name = "Kotlin" // 自动推断为 String
val score = 95 // 自动推断为 Int
val ratio = 0.9 // 自动推断为 Double
//场景2:算术运算符重载处理
val l = 1L + 3 // Long + Int => 得到的是Long
println(l is Long) // 使用is判断l的类型 输出:true
}
Kotlin 刻意设计为不支持隐式类型转换(Implicit Conversions),这与 Java、C++ 等语言不同。以下是关键原因和典型案例:
案例1:整数与浮点数
Kotlin
val price: Double = 10 // 编译错误!必须显式转换
// 正确做法:
val correctPrice: Double = 10.toDouble()
10 是 Int 类型,直接赋值给 Double 会导致精度意外丢失的风险。Kotlin 强制要求开发者明确转换意图。对比 Java 的隐式转换:
java
double price = 10; // Java 自动隐式转换(可能掩盖潜在问题)
案例2:数字超范围
Kotlin
val bigNumber: Long = 10000000000 // 编译错误!超出 Int 范围
// 必须显式声明为 Long:
val correctNumber = 10000000000L
设计哲学 :Kotlin 要求开发者明确知晓自己在处理大数字,避免数值溢出等隐蔽问题。
1.6 数字运算
Kotlin支持数字运算的标准集:+、 -、 *、 /、 %
Kotlin
fun main() {
println(1 + 2)
println(2_500_000_000L - 1L)
println(3.14 * 2.71)
println(10.0 / 3)
}
1.7 整数除法
整数间的除法总是返回整数。会丢弃任何小数部分。
Kotlin
fun main() {
val x = 5 / 2
println(x == 2.5) //会提示:操作符'=='不能应用于'Int'和'Double'
println(x == 2) // 只保留整数部分 输出:true
}
对于任何两个整数类型之间的除法来说都是如此:
Kotlin
fun main() {
val x = 5L / 2
println (x == 2) //提示错误,因为x此时是Long类型,因为不能将Long (x)与Int(2)进行比较
println(x == 2L) // true
}
如需返回带小数部分的除法结果,请将其中的一个参数显式转换为浮点类型:
Kotlin
fun main() {
val x = 5 / 2.toDouble() //这种做法会让x被推测为Double类型
println(x == 2.5)
}
2.布尔
Boolean 类型表示可以有 true 与 false 两个值的布尔对象
Kotlin
fun main() {
val myTrue: Boolean = true
val myFalse: Boolean = false
val boolNull: Boolean? = null
println(myTrue || myFalse) // true
println(myTrue && myFalse) // false
println(!myTrue) // false
println(boolNull) // null
}
||和&&操作符是惰性的,这意味着:
- 如果第一个操作数为真,||运算符不计算第二个操作数。
- 如果第一个操作数为假,&&操作符不对第二个操作数求值。
3.字符
字符用 Char 类型表示。 字符字面值用单引号括起来: '1'
3.1 特殊字符
特殊字符可以以转义**反斜杠 \**开始。 支持这几个转义序列:
- \t------制表符
- \b------退格符
- \n------换行(LF)
- \r------回车(CR)
- \'------单引号
- \"------双引号
- \\------反斜杠
- \$------美元符
- 如果需要编码其他字符,也可以用 Unicode 转义序列语法:'\uFF00'。
Kotlin
fun main() {
val aChar: Char = 'a'
println(aChar)
println('\n') // 输出一个额外的换行符
println('\uFF00')
}
如果字符变量的值是数字,也可以使用 digitToInt() 函数将其显式转换为 Int 数字。
4.字符串
Kotlin 中字符串用 String 类型表示,既保留了 Java 的强大功能,又通过扩展函数和语言特性大幅提升了易用性
4.1 基础特性
(1) 不可变性:与 Java 相同,Kotlin 字符串是不可变的
Kotlin
val s = "hello"
s[0] = 'H' // 编译错误!String不可变
(2) 两种字面量
- 转义字符串:支持转义字符
Kotlin
val path = "C:\\Windows\\System32"
- 原始字符串(Raw String):三引号包裹,保留所有格式
Kotlin
val json = """
{
"name": "Kotlin",
"age": 10
}
""".trimIndent()
4.2 常用操作
- 比 Java 更强大的插值功能
Kotlin
val name = "Alice"
val greeting = "Hello, $name!" // 插入简单变量
val lengthInfo = "Name length: ${name.length}" // 插入表达式
println("1 + 2 = ${1 + 2}") // 输出:1 + 2 = 3
- 拼接与重复
Kotlin
val combined = "Hello" + " " + "World" // 传统方式
val repeated = "Na".repeat(3) + " Batman!" // 输出:NaNaNa Batman!
- 多行字符串处理
Kotlin
val poem = """
|床前明月光,
|疑是地上霜。
|举头望明月,
|低头思故乡。
""".trimMargin("|") // 自动去除前导|和缩进
- 分割与连接
Kotlin
val langs = "Kotlin,Java,Swift".split(",") // 拆分为List
val merged = langs.joinToString(" | ") // 合并为"Kotlin | Java | Swift"
4.3 扩展函数增强
除了常用操作以外,Kotlin 为字符串添加了大量实用扩展(展示部分,感兴趣可以查API文档或者问AI):
|-----------------------|------|--------------------------------------------------|
| 函数 | 作用 | 示例 |
| substringBefore/After | 截取子串 | "file.txt".substringBefore(".") → "file" |
| padStart/padEnd | 填充对齐 | "5".padStart(2, '0') → "05" |
| toIntOrNull | 安全转换 | "123".toIntOrNull() → 123 |
| isBlank | 空值检查 | " ".isBlank() → true |
| replace | 正则替换 | "123-456".replace(Regex("\\D"), "") → "123456" |
4.4 性能优化技巧
- 字符串构建器
Kotlin
val sb = buildString {
append("Hello")
append(" ")
append("World")
}
// 比连续"+"拼接效率更高
- 避免重复创建
Kotlin
// 错误做法(每次循环创建新对象)
var result = ""
for (i in 1..1000) {
result += i.toString()
}
// 正确做法
val betterResult = StringBuilder().apply {
for (i in 1..1000) {
append(i)
}
}.toString()
4.5 与Java互操作
- 无缝调用Java字符串方法
Kotlin
val javaStyle = "Kotlin".toLowerCase(Locale.ROOT)
- 消除空指针隐患
Kotlin
val maybeString: String? = getJavaString()
val safeLength = maybeString?.length ?: 0 // 安全访问
//输出结果如下:
//当getJavaString() 返回 null 时 → safeLength = 0
//当返回空串"" 时 → safeLength = 0
//当返回有效字符串时 →safeLength = 字符串长度
三、控制语句
kotlin拥有比Java更智能的流程控制
1.条件语句(if 与 when)
Kotlin 的 if 不仅是语句,还可以是表达式(有返回值),返回值由每个分支的最后一行的表达式决定:
- 注意,这种表达式型的写法, if 必须有 else 分支,否则会报错
- Kotlin 的 if 是表达式,因此它天然就会产生值,无论是否被接,比如下面的表达式:
- 有变量接受,返回值就有意义。所以,当需要返回值时,必须显式指定接收位置
- 没有变量接受,返回值就没有意义(直接丢弃)
Kotlin
// 作为表达式返回最大值
val max = if (a > b) {
println("a 更大")
a // 最后一行的表达式会自动成为该分支的返回值
} else {
b
}
// 替代 Java 的三元运算符,
val status = if (score >= 60) "通过" else "未通过"
when 是增强版 switch,支持任意类型和复杂条件:
Kotlin
//常量值匹配,类似于switch
when (status) {
200 -> println("成功")
404 -> println("未找到")
500 -> println("服务器错误")
else -> println("未知状态")
}
//类型检查、智能转换:使用 `is` 或 `!is` 来检查对象的类型,并且在分支内自动智能转换为该类型
when (shape) {
is Circle -> println("半径: ${shape.radius}") // 自动转为 Circle
is Square -> println("边长: ${shape.side}") // 自动转为 Square
!is Int -> println("不是整数")
}
//范围匹配
when (score) {
in 90..100 -> println("A")
in 80..89 -> println("B")
!in 60..100 -> println("不及格")
else -> println("未知类型")
}
// 作为表达式使用,直接输出结果,并且使用变量接收
val description = when {
temperature > 30 -> "炎热"
temperature < 10 -> "寒冷"
else -> "舒适"
}
2.循环语句
kotlin的循环语句简介且强大
2.1 遍历范围(Range)
Kotlin
// 基础、常规
for (i in 1..5) {
print("$i ") // 输出: 1 2 3 4 5
}
// 反向
for (i in 5 downTo 1) {
print("$i ") // 输出: 5 4 3 2 1
}
// 带步长
for (i in 1..10 step 2) {
print("$i ") // 输出: 1 3 5 7 9
}
// 半开范围(不包含结束值)
for (i in 1 until 5) {
print("$i ") // 输出: 1 2 3 4
}
2.2 遍历数组
Kotlin
val numbers = arrayOf(10, 20, 30)
// 遍历元素
for (num in numbers) {
print("$num ") // 输出: 10 20 30
}
// 带索引遍历
for (index in numbers.indices) {
println("索引 $index 的值是 ${numbers[index]}")
}
// 输出:
// 索引 0 的值是 10
// 索引 1 的值是 20
// 索引 2 的值是 30
2.3 遍历集合(List/Set)
Kotlin
val fruits = listOf("Apple", "Banana", "Orange")
// 基础遍历
for (fruit in fruits) {
print("$fruit ") // 输出: Apple Banana Orange
}
// 带索引遍历
//val iterable = fruits.withIndex() 是kotlin当中的一个可迭代对象
//当我们在for循环中使用了iterable对象,实际上是发生了下面这几件事:
1、隐式调用iterable.iterator(),内部会产生index
2、循环开始时:检查iterator.hasNext()
3、每次迭代:获取item = iterator.next()
for ((index, fruit) in fruits.withIndex()) {
println("$index: $fruit")
}
// 输出:
// 0: Apple
// 1: Banana
// 2: Orange
2.4 遍历 Map
Kotlin
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
// 遍历键值对
for ((key, value) in map) {
println("$key -> $value")
}
// 输出:
// a -> 1
// b -> 2
// c -> 3
// 仅遍历键
for (key in map.keys) {
print("$key ") // 输出: a b c
}
// 仅遍历值
for (value in map.values) {
print("$value ") // 输出: 1 2 3
}
2.5 遍历字符串字符
Kotlin
val text = "Kotlin"
// 遍历字符
for (char in text) {
print("$char-") // 输出: K-o-t-l-i-n-
}
// 带索引遍历
for ((i, char) in text.withIndex()) {
println("$i: $char")
}
// 输出:
// 0: K
// 1: o
// 2: t
// 3: l
// 4: i
// 5: n
2.6 自定义迭代器
系统提供的功能能应对多数场景,但kotlin也提供了自定义迭代器,实现Iterable接口的类可被遍历,对自定义迭代器感兴趣,后续可自行扩展学习
Kotlin
//定义了一个名为 Counter 的自定义迭代器类,它实现了 Iterable<Int> 接口
class Counter(val max: Int) : Iterable<Int> {
override fun iterator(): Iterator<Int> = object : Iterator<Int> {
private var current = 0 // 当前计数值
// 检查是否还有下一个值
override fun hasNext() = current < max
// 获取下一个值并移动
override fun next() = current++
}
}
// 使用时,指定max的值是3,然后自定义迭代器就会根据你定义的条件进行遍历
for (i in Counter(3)) {
print("$i ") // 输出: 0 1 2
}
2.7 控制流操作
使用continue、break进行流程控制
Kotlin
// 跳过当前迭代(continue)
for (i in 1..5) {
if (i == 3) continue
print("$i ") // 输出: 1 2 4 5
}
// 中断循环(break)
for (i in 1..10) {
if (i > 5) break
print("$i ") // 输出: 1 2 3 4 5
}
// 标签跳转(控制外层循环)
outer@ for (i in 1..3) {
for (j in 1..3) {
if (i == 2 && j == 2) break@outer
print("($i,$j) ")
}
}
// 输出: (1,1) (1,2) (1,3) (2,1)
2.8 标签控制
精准控制循环跳转:
Kotlin
outer@ for (i in 1..3) {
inner@ for (j in 1..3) {
if (i == 2 && j == 2) break@outer
print("($i,$j) ") // 输出: (1,1) (1,2) (1,3) (2,1)
}
}
2.9 与 Java 对比
|-------|---------------------------|-----------------------|-------------|
| 特性 | Java | Kotlin | 优势 |
| 条件返回值 | 三元运算符 a>b ? a : b | if (a>b) a else b | 支持多行逻辑 |
| 多分支条件 | switch-case | when 表达式 | 支持任意类型和复杂条件 |
| 范围循环 | for (int i=0; i<10; i++) | for (i in 0 until 10) | 避免索引越界错误 |
四、函数
1.函数的定义
- 函数声明使用 fun 关键字定义:
Kotlin
fun add(a: Int, b: Int): Int {
return a + b
}
- 参数格式:参数名: 类型
- 返回值类型在参数后声明(如 : Int)
- 表达式函数(单表达式)
省略 {} 和 return:
Kotlin
fun add(a: Int, b: Int): Int = a + b
// 类型可自动推断,进一步简化:
fun add(a: Int, b: Int) = a + b
- 参数支持默认值:
Kotlin
fun greet(name: String = "Guest") {
println("Hello, $name!")
}
调用:greet() 输出 Hello, Guest!
- 调用时,明确参数名:
Kotlin
fun createUser(name: String, age: Int, isAdmin: Boolean = false) { ... }
// 指定参数名(可乱序)
createUser(age = 25, name = "Alice")
- 可变参数,类似 Java 的 ...
Kotlin
fun sum(vararg numbers: Int): Int {
return numbers.sum()
}
sum(1, 2, 3) // 输出 6
- 顶层函数,可脱离类直接定义在文件中:
Kotlin
// File: MathUtils.kt
fun multiply(a: Int, b: Int) = a * b
无需像 Java 的静态工具类。
- 局部函数,内部嵌套函数:
Kotlin
fun outer() {
fun inner() { println("Inner") }
inner()
}
2.扩展函数
扩展函数(Extension Functions) 是一种在不修改原类定义的情况下,为现有类添加新功能的机制。它们通过静态解析实现,不会侵入原类代码,但能像类成员一样被调用。
- 语法结构
Kotlin
fun ReceiverType.functionName(params): ReturnType {
// 函数体
// 通过 `this` 访问接收者对象(可省略)
}
- ReceiverType: 要扩展的类(接收者类型)
- functionName: 扩展函数的名称
- this: 在函数体内指向接收者对象实例(隐式可用)
- 本质是扩展函数是静态解析的静态函数(编译时确定),不会修改原类,而是通过编译器转换为静态方法调用。例如:
Kotlin
// 编译后实际会生成:
fun String.lastChar() → public static char lastChar(String receiver)
2.1 使用案例
- 为String扩展一个函数,获取o在字符串中第一次出现的位置
Kotlin
/**
* 获取o在字符串中第一次出现的位置,如果没出现就返回-1
*/
fun String.getOIndex(): Int {
// 函数体
// 如果在这里面调用this,`this` 指代调用该方法的字符串对象,另外,this也可以被省略
return this.indexOf("o")
}
//调用
val s :String= "hello"
val oIndex = s.getOIndex()
println("o出现的位置是:$oIndex")
- View扩展:简化 Android 中的视图控制
Kotlin
fun View.show() {
this.visibility = View.VISIBLE
}
fun View.hide() {
this.visibility = View.GONE
}
// 使用
myButton.show() // 等价于 myButton.visibility = View.VISIBLE
- 可空类型扩展:安全处理空值
Kotlin
fun String?.safeLength(): Int {
return this?.length ?: 0 // 若为 null 返回 0
}
fun main() {
val s: String? = null
println(s.safeLength()) // 输出:0(不会抛 NPE)
}
3.高阶函数
高阶函数 是指可以接受函数作为参数、或者返回一个函数的函数。将行为作为参数传递,使代码更灵活且可复用,同时保持类型安全。极大地增强了代码的抽象能力和灵活性。
3.1 语法
Kotlin
// 定义高阶函数
fun 函数名(参数名: (参数类型) -> 返回类型) {
// 调用传入的函数
参数名(实参)
}
3.2 使用案例
- 基础回调函数,把函数当成参数传递使用
Kotlin
// 定义高阶函数
fun performRequest(
url: String,
onSuccess: (response: String) -> Unit, // 函数类型参数,Unit表示函数不返回任何有意义的值,类似于java的void
onFailure: (exception: Exception) -> Unit // 函数类型参数
) {
try {
// 模拟网络请求
val response = "响应数据: $url"
onSuccess(response) // 调用成功回调
} catch (e: Exception) {
onFailure(e) // 调用失败回调
}
}
// 使用
fun main() {
performRequest(
url = "https://api.example.com",
onSuccess = { response ->
println("成功: $response")
},
onFailure = { error ->
println("失败: ${error.message}")
}
)
}
- 带返回值的函数参数
Kotlin
// 定义高阶函数
fun transformData(
data: List<Int>,
transform: (Int) -> String // 接收Int返回String的函数
): List<String> {
return data.map(transform) // 使用传入的转换函数
}
// 使用
fun main() {
val numbers = listOf(1, 2, 3)
val result = transformData(numbers) { num ->
"数字: ${num * 2}" // Lambda 表达式
}
println(result) // 输出: [数字: 2, 数字: 4, 数字: 6]
}
- 返回函数的函数
Kotlin
// 定义高阶函数(返回一个函数)
fun createLogger(prefix: String): (String) -> Unit {
return { message -> // 返回一个Lambda表达式
println("[$prefix] $message")
}
}
// 使用
fun main() {
val activityLogger = createLogger("MainActivity")
val networkLogger = createLogger("Network")
activityLogger("Activity 已创建") // 输出: [MainActivity] Activity 已创建
networkLogger("请求发送") // 输出: [Network] 请求发送
}
返回类型:(String) -> Unit
- 表示它返回一个函数
- 这个返回的函数:接受一个 String 参数,返回Unit
五、空安全机制
在 Kotlin 中,? 和 !!都是空安全机制的核心组成部分,用于处理可能为 null 的值。这是 Kotlin 区别于 Java 的重要特性,能有效减少 NullPointerException(空指针异常)。
1.问号(?)的作用与用法
- 声明可空类型
Kotlin
var name: String? = null // 声明一个可能为 null 的字符串
不加 ? 的类型默认为非空类型(不能赋 null 值):
Kotlin
var age: Int = null // 编译错误:Null can not be a value of a non-null type Int
- 安全调用操作符(?.)
Kotlin
val length: Int? = text?.length // 如果 text 为 null,返回 null 而不报错
等价于 Java 的:
java
Integer length = text != null ? text.length() : null;
- 安全转换(as?)
安全的类型转换,失败时返回 null 而非抛异常,避免出现避免 ClassCastException
Kotlin
val num: Int? = obj as? Int // 转换失败时返回 null 而不是抛异常
- Elvis 操作符(?:)
左侧表达式为 null 时可以提供一个默认值,简化空值处理
Kotlin
val name = user?.name ?: "Unknown" // 若左边为 null,则使用右侧默认值
- 先计算 ?: 左侧表达式
- 如果结果非空 -> 直接返回该值
- 如果结果为 null -> 返回右侧表达式结果
- 非空断言操作符
这个慎用,因为会导致空指针:告诉编译器,我确定这个值不会为null,哪怕真的为null,你也可以直接抛空指针
Kotlin
val length: Int = text!!.length // 断言 text 不为 null,若为 null 则立即抛 NPE
2.为什么需要这些符号?
通过类型系统强制开发者显式处理空值,将运行时可能出现的 NPE 转为编译期错
|----------------|-----------------------|-----------------------|-----------------------|
| 场景 | Java | Kotlin(安全) | Kotlin(危险) |
| 访问可能为 null 的对象 | text.length()→ 可能 NPE | text?.length→ 返回 null | text!!.length→ 可能 NPE |
| 优势 | 编译时不检查空值 | 编译时强制处理空值 | 开发者主动承担风险 |
3.最佳实践示例
3.1 安全处理空值
Kotlin
// 链式安全调用 + Elvis 操作符
val cityName = user?.address?.city ?: "China"
// 配合 let 作用域函数
//为对象创建一个临时作用域,在内部安全操作非空对象
user?.let {
println("Name: ${it.name}") // 仅在非 null 时执行
}
4.关键总结
|-----|---------------------------|------|
| 操作符 | 含义 | 风险程度 |
| ? | "这可能为 null" | 无风险 |
| ?. | "为 null 时跳过" | 无风险 |
| ?: | "为 null 时使用默认值" | 无风险 |
| !! | "我保证这里不为 null!" | 高风险 |
终极建议:
- 90% 的场景使用 ?. 和 ?: 替代 !!
- 剩余 10% 与 Java 交互的代码中,使用 !! 前务必确认数据源的非空性
- 永远不要为了"快速完成代码"而滥用 !!