Kotlin的变量声明体系是其语法简洁性与安全性的基础,核心在于通过val(不可变)与var(可变)的严格区分,引导开发者采用不可变编程思想。与Java相比,Kotlin省略了显式的访问修饰符(默认public)和类型声明(支持自动推断),显著减少了冗余代码[4]。
不可变变量(val)val声明的变量在初始化后无法重新赋值,对应Java中的final变量。这种设计强制约束状态变化,减少并发场景下的数据竞争风险,是Kotlin推荐的变量声明方式。其语法格式为:
// 显式指定类型(推荐用于复杂场景,提升可读性)
val userName: String = "Kotlin"
// 类型自动推断(基础类型或简单场景)
val userAge = 30 // 推断为Int类型
// 编译错误:val变量不可二次赋值
userName = "NewName" // Error: Val cannot be reassigned
可变变量(var)var声明的变量允许后续修改,但其使用需谨慎,仅在明确需要状态变化时采用(如计数器、动态配置)。Android开发中过度使用var可能导致Activity/Fragment的状态管理混乱,增加调试难度。示例:
// 可变变量声明
var score: Int = 0
score += 10 // 允许修改:score变为10
// 类型推断与可空性结合
var nullableName: String? = "Alice" // 可空类型(String?)
nullableName = null // 合法:可空类型允许赋值null
延迟初始化(lateinit var)
对于非空类型但无法在声明时初始化的变量(如依赖注入对象、Activity中的视图),Kotlin提供lateinit关键字延迟初始化。需注意:lateinit仅支持var且不能用于基本类型(如Int,需用Int?或Delegates.notNull()替代)[4]。示例:
class MainActivity : AppCompatActivity() {
// 延迟初始化视图(onCreate中赋值)
private lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
recyclerView = findViewById(R.id.recycler_view) // 必须初始化,否则抛异常
}
}
空安全类型系统
Kotlin通过类型系统强制区分非空类型(如String)与可空类型(如String?),从编译期避免NullPointerException(NPE)。Android官方数据显示,该特性可使应用崩溃率降低20%[5]。核心语法包括:
?表示允许为null // 可空类型变量
val nullableString: String? = null
// 安全调用:若nullableString为null,返回null而非抛出NPE
val length = nullableString?.length // 结果为null(Int?类型)
// 非空断言:仅在确定对象非空时使用
val forcedLength = nullableString!!.length // 抛出NullPointerException
最佳实践:
val实现,仅在确需状态变化时使用var by lazy(懒加载)或可空类型+空判断 !!场景可通过安全调用(?.)或Elvis运算符(?:)优化
// Elvis运算符示例:为空时返回默认值
val safeLength = nullableString?.length ?: -1 // 结果为-1
Kotlin的函数体系通过灵活的语法设计实现了代码精简与可读性的平衡,核心特性包括表达式体函数、命名参数、默认参数等,显著优于Java的函数定义方式。
函数基本结构
函数声明以fun关键字开头, followed by 函数名、参数列表(参数名: 类型)、返回类型(:后指定,无返回值可省略或显式Unit)和函数体。示例:
// 完整函数定义(含返回类型和代码块)
fun add(a: Int, b: Int): Int {
return a + b
}
// 无返回值函数(Unit可省略)
fun printSum(a: Int, b: Int): Unit {
println("Sum: ${a + b}")
}
// 简化无返回值函数
fun log(message: String) {
println("[LOG] $message")
}
单表达式函数
对于仅含单个表达式的函数,Kotlin允许省略花括号和return关键字,直接用=连接参数与表达式结果。这种语法使代码行数减少50%以上,尤其适合工具类函数或简单计算[6]。示例:
// 等价于add函数的单表达式形式
fun add(a: Int, b: Int) = a + b
// 带条件判断的单表达式函数
fun getAbsoluteValue(num: Int) = if (num >= 0) num else -num
// 复杂表达式(类型自动推断为Boolean)
fun isEven(num: Int) = num % 2 == 0
命名参数与默认参数
Kotlin支持调用函数时显式指定参数名,解决了Java中多参数函数调用时的"参数顺序依赖"问题。结合默认参数,可实现"可选参数"效果,避免Java中重载函数的冗余定义。示例:
// 带默认参数的函数
fun greet(name: String, title: String = "Mr.", suffix: String = "!"): String {
return "$title $name$suffix"
}
// 调用方式对比
greet("Smith") // 使用默认参数:"Mr. Smith!"
greet("Alice", "Ms.") // 按位置传参:"Ms. Alice!"
greet(name = "Bob", suffix = "!!!") // 命名参数跳过默认值:"Mr. Bob!!!"
greet(suffix = "?", name = "Charlie") // 命名参数无序传递:"Mr. Charlie?"
空安全函数设计
函数参数和返回值的可空性需显式声明,确保调用方正确处理空值。例如,将字符串转换为整数时,toIntOrNull()返回Int?类型,强制调用方处理转换失败场景[7]。示例:
// 可空返回值函数
fun stringToInt(str: String): Int? {
return str.toIntOrNull() // 失败时返回null
}
// 调用方必须处理空值
val number = stringToInt("123") // Int?类型
if (number != null) {
println("Valid number: $number")
} else {
println("Invalid input")
}
函数设计最佳实践:
fun f() = ...) Kotlin的控制流结构在Java基础上进行了革命性优化,when表达式替代了传统switch-case,支持任意类型匹配、范围判断和表达式返回值;字符串模板则通过简洁的插值语法,避免了Java中字符串拼接的繁琐。
when表达式when是Kotlin最具特色的控制流结构,相比Java的switch-case具有三大优势:
break,避免"穿透执行"错误 基础用法示例:
// 匹配具体值(等价于Java switch)
fun getDayOfWeek(day: Int): String = when (day) {
1 -> "Monday"
2 -> "Tuesday"
3, 4 -> "Midweek" // 多值匹配(3或4)
in 5..7 -> "Weekend" // 范围匹配(5-7)
else -> "Invalid day" // 必须覆盖所有情况
}
// 条件判断模式(无参数when)
fun getGreeting(hour: Int): String = when {
hour in 6..11 -> "Good morning" // 6:00-11:59
hour in 12..17 -> "Good afternoon" // 12:00-17:59
hour < 6 || hour > 23 -> "Good night"
else -> "Good evening"
}
// 对象类型匹配(高级用法)
fun processData(data: Any): String = when (data) {
is String -> "String length: ${data.length}"
is Int -> "Int value: $data"
is List<*> -> "List size: ${data.size}"
else -> "Unknown type"
}
循环结构
Kotlin保留了for、while循环,但通过区间表达式和迭代器扩展增强了易用性。其中repeat(n)函数是快速实现固定次数循环的语法糖,特别适合初始化逻辑或测试场景[8]。示例:
// 传统for循环(迭代区间)
for (i in 1..5) { // 闭区间:1,2,3,4,5
print(i)
}
// 步长控制(间隔2)
for (i in 10 downTo 1 step 2) { // 10,8,6,4,2
print(i)
}
// repeat函数(固定次数循环)
repeat(3) { index -> // 索引从0开始:0,1,2
println("Loop $index: Hello")
}
// 集合迭代
val fruits = listOf("Apple", "Banana", "Cherry")
for ((index, fruit) in fruits.withIndex()) {
println("$index: $fruit") // 输出"0: Apple", "1: Banana", "2: Cherry"
}
字符串模板
Kotlin通过$符号实现字符串插值,避免了Java中+拼接的低效与冗长。基础语法支持直接插入变量,复杂表达式则需用${}包裹;Kotlin 2.2进一步增强为多美元插值,允许在单个模板中嵌套对象属性或函数调用[9]。
基础用法与常见错误:
// 基础变量插值
val name = "Alice"
val age = 25
val basicInfo = "Name: $name, Age: $age" // 结果:"Name: Alice, Age: 25"
// 复杂表达式插值(需用${})
val price = 19.99
val quantity = 3
val total = "Total: $${price * quantity}" // 错误:$后直接跟表达式需用{}
val correctTotal = "Total: $${(price * quantity).toInt()}" // 正确:$${59}($符号需转义)
// 对象属性插值(Kotlin 2.2多美元特性)
data class User(val name: String, val age: Int)
val user = User("Bob", 30)
val userInfo = "User ${user.name} (${user.age})" // 结果:"User Bob (30)"
// 常见错误:缺少变量名
val invalidTemplate = "Value: $" // 编译警告:Unresolved reference: (需指定变量)
val validTemplate = "Value: ${price}" // 正确:"Value: 19.99"
控制流与模板最佳实践:
else分支),避免编译错误[10] StringBuilder或buildString函数
val longText = buildString {
append("Hello, ")
append(name)
append("! Your score is ")
append(score)
}
>=和<=,如hour in 9..18比hour >=9 && hour <=18更直观空安全是Kotlin对Java的重大改进,通过类型系统强制区分可空与非空类型,从源头减少NPE。其核心机制包括可空类型标记(?)、安全调用(?.)、非空断言(!!)和 Elvis运算符(?:)[11]。
可空类型操作
// 安全调用链(任意环节为null则整体返回null)
val street = user?.address?.street // 若user或address为null,结果为null
// Elvis运算符(为空时返回默认值)
val userName = nullableUser?.name ?: "Guest" // 等价于if (nullableUser != null) nullableUser.name else "Guest"
// 非空断言(强制非空,风险操作)
val nonNullName = nullableName!! // 若nullableName为null,抛出NullPointerException
自定义异常
Kotlin的异常体系与Java兼容,通过继承Exception或RuntimeException实现自定义异常。语法简洁,无需显式throws声明,异常传播更灵活[12]。示例:
// 自定义检查型异常(编译期检查)
class InvalidInputException(message: String) : Exception(message)
// 自定义运行时异常(运行期抛出)
class NetworkException(message: String) : RuntimeException(message)
// 抛出与捕获异常
fun validateInput(input: String) {
if (input.isEmpty()) {
throw InvalidInputException("Input cannot be empty")
}
}
fun main() {
try {
validateInput("")
} catch (e: InvalidInputException) {
println("Error: ${e.message}") // 输出:Error: Input cannot be empty
} finally {
println("Validation completed") // 始终执行
}
}
Kotlin编译器虽以严格著称,但仍存在易犯的语法错误,需特别注意以下场景:
1. 变量作用域问题
fun calculate() {
if (condition) {
val result = 10 // 局部变量仅在if块内可见
}
println(result) // 编译错误:Unresolved reference: result
}
规避:将变量声明提升至外层作用域,或使用表达式返回值。
2. when表达式缺少else分支
fun getGrade(score: Int): Char = when (score) {
in 90..100 -> 'A'
in 80..89 -> 'B'
// 缺少else分支,编译错误:'when' expression must be exhaustive
}
规避:添加else分支处理默认情况,或确保覆盖所有可能值(如枚举匹配)。
3. 可空类型直接使用
fun printLength(str: String?) {
println(str.length) // 编译错误:Only safe (?.) or non-null asserted (!!.) calls are allowed on nullable receiver
}
规避:使用安全调用(str?.length)或非空断言(str!!.length,谨慎使用)。
4. 字符串模板转义错误
val price = "$$19.99" // 错误:$符号未转义,编译器尝试解析变量19.99
val correctPrice = "$${19.99}" // 正确:第一个$转义,输出"$19.99"
规避:使用$转义$符号,复杂表达式用${}包裹。
通过以上语法规则的系统学习,开发者可充分利用Kotlin的简洁性与安全性,显著提升Android项目的开发效率与代码质量。建议结合官方中文文档深入实践,逐步掌握高级特性如作用域函数、委托模式等[13]。