Kotlin 函数和变量(四)
导读大纲
- 1.1 基本要素: 函数和变量
- 1.1.1 声明变量以存储数据
- 1.1.2 将变量标记为只读或可重新赋值
- 1.1.3 更简单的字符串格式化: 字符串模板
1.1 基本要素: 函数和变量
-
本节将向你介绍每个 Kotlin 程序都包含的基本元素: 函数和变量
- 你将编写自己的第一个 Kotlin 程序,了解 Kotlin 如何让你省略许多类型声明
- 以及它如何鼓励你尽可能避免使用可变数据–为什么这是件好事
- 你将编写自己的第一个 Kotlin 程序,了解 Kotlin 如何让你省略许多类型声明
-
承上–函数传送门
1.1.1 声明变量以存储数据
-
在所有 Kotlin 程序中,另一个常用的基本构件是变量,它允许你存储数据
- Kotlin 中的变量声明以一个关键字(val 或 var)开头
- 然后是变量的名称
- 虽然 Kotlin 可以让你省略许多变量声明的类型
- 这得益于其强大的类型推断功能
- 但你总是可以在变量名后显式地加上类型
- Kotlin 中的变量声明以一个关键字(val 或 var)开头
-
例如,如果您需要在一个 Kotlin 变量中存储一个最著名的问题及其相应的答案
- <1> 可以通过指定两个变量 question 和 answer 来实现
- 并在变量名后显式地写上它们的类型
- <2> 也可以省略类型声明,使示例更加简洁
- 与表达式体函数一样,如果不指定类型
- 编译器会分析初始化表达式,并将其类型作为变量类型
- 这里初始化表达式 42 的类型是 Int,因此变量 answer 的类型也是 Int
- 与表达式体函数一样,如果不指定类型
- <3> 如果使用浮点常量,变量的类型就是 Double
- <1> 可以通过指定两个变量 question 和 answer 来实现
// <1>
val question: String = "The Ultimate Question of Life, the Universe, and Everything"
val answer: Int = 42
// =========================== <2>
val question = "The Ultimate Question of Life, the Universe, and Everything"
val answer = 42
val yearsToCompute = 7.5e6
- 如果您没有立即初始化变量
- 而是在稍后才赋值,编译器将无法推断出变量的类型
- 在这种情况下,您需要明确指定变量的类型
- 否则会报错: Return type for property answer cannot be resolved
- 而是在稍后才赋值,编译器将无法推断出变量的类型
fun main() {
val answer: Int
answer = 42
}
1.1.2 将变量标记为只读或可重新赋值
-
为了控制变量何时可以分配新值,Kotlin提供两个关键字val和var来声明变量
- val(from value)声明一个只读引用
- 用 val 声明的变量只能赋值一次,初始化后,就不能再赋值
- 相比之下,在 Java 中,这可以通过 final 修饰符来表示
- 用 val 声明的变量只能赋值一次,初始化后,就不能再赋值
- var(from variable)声明一个可重新赋值的引用
- 您可以将即使在初始化之后,也可以向此类变量添加其他值
- 这种行为类似于 Java 中的non-final变量
- 您可以将即使在初始化之后,也可以向此类变量添加其他值
- val(from value)声明一个只读引用
-
默认情况下,在 Kotlin 中应尽量使用 val 关键字声明所有变量
- 只有在必要时才将其改为 var
- 使用只读引用、不可变对象和无副作用的函数
- 可以让你充分利用函数式编程风格带来的好处
-
在执行定义 val 变量的代码块时,必须对该变量进行一次精确的初始化
- 不过,只要编译器能确保只执行其中一条初始化语句
- 你也可以根据某些条件用不同的值来初始化它
- 您可能会遇到这样的情况:您要为一个结果变量赋值
- 而这个结果变量的内容取决于另一个函数的返回值
- <1> 编译器很聪明,知道两个可能的赋值中会有一个被执行
- 因此您仍然可以使用 val 关键字将 result 指定为只读引用
- 不过,只要编译器能确保只执行其中一条初始化语句
fun canPerformOperation(): Boolean {
return true
}
fun main() {
val result: String // <1>
if (canPerformOperation()) {
result = "Success" // <1>
} else {
result = "Can't perform operation" // <1>
}
}
- 需要注意的是,尽管val引用本身是只读的,一旦赋值就不能更改
- 但它指向的对象可能是可变的
- <1> 将一个元素添加到一个可变的列表中
- 而该列表是由一个只读引用定义的,这完全没有问题
fun main() {
val languages = mutableListOf("Java")
languages.add("Kotlin")
}
- 尽管 var 关键字允许变量改变其值,但其类型是固定的
- 例如,如果你在程序进行到一半时决定答案变量应该存储字符串而不是整数
- 那么你就会遇到编译错误
- 那么你就会遇到编译错误
- 编译器仅通过初始化器推断变量类型,在确定类型时不会考虑后续赋值
- 如果需要在变量中存储类型不匹配的值
- 则必须手动将该值转换或强制转换为正确的类型
- 例如,如果你在程序进行到一半时决定答案变量应该存储字符串而不是整数
1.1.3 更简单的字符串格式化: 字符串模板
- 使用字符串模板来拼接字符串
- 在代码中, 您通过 readln() 函数(可在任何 Kotlin 文件中使用)
- 从标准输入流中读取输入
- 然后使用 if 表达式声明一个变量名并初始化其值
- 如果标准输入流存在且不为空,那么 name 就会被赋值为 input
- 否则, 它的默认值为"Kotlin"
- 最后, 在传递给 println 的字符串字面量中使用它
- 如果标准输入流存在且不为空,那么 name 就会被赋值为 input
- 在代码中, 您通过 readln() 函数(可在任何 Kotlin 文件中使用)
fun main() {
val input = readln()
val name = if (input.isNotBlank()) input else "Kotlin"
println("Hello, $name!")
}
-
与许多脚本语言一样,Kotlin 允许在变量名前加上 $ 字符
- 从而在字符串字面量中引用局部变量, 更紧凑,效率也一样高
- 这等同于 Java 的字符串连接(“Hello, " + name +”!")
- 当然,表达式是经过静态检查的,如果您试图引用一个不存在的变量
- 代码将无法编译
- 注意,对于 JVM 1.8 目标,编译后的代码会创建一个 StringBuilder
- 并将常量部分和变量值附加到其中
- 针对 JVM 9 或更高版本的应用程序
- 会通过 invokedynamic 将字符串连接编译成更高效的动态调用
- 从而在字符串字面量中引用局部变量, 更紧凑,效率也一样高
-
如果需要在字符串中包含 $ 字符,可以用反斜杠将其转义
- <1> 不将 x 解释为变量引用
fun main() {
println("\$x") // <1>
}
- Kotlin 中的字符串模板非常强大,因为它们不会限制只能引用单个变量
- 你也可以在字符串模板中提供一个更复杂的表达式
- 只需在表达式周围加上大括号即可
- <1> 使用 ${} 语法在问候语字符串中插入 name.length 属性
- 你也可以在字符串模板中提供一个更复杂的表达式
fun main() {
val name = readln()
if (name.isNotBlank()) {
println("Hello, ${name.length}-letter person!") // <1>
}
// Blank input: (no output)
// "Seb" input: Hello, 3-letter person!
}
- 现在,你可以在字符串模板中包含任意表达式
- 而且你已经知道 if 在 Kotlin 中也是一种表达式
- 将两者结合起来, 将条件直接包含在字符串模板中
- <1> 在字符串模板中,您仍然可以使用本身包含双引号字符串的表达式
- 当表达式被求值时,它会返回字符串"someone"或提供的名称
- 并将其插入到包围的模板中
- 当表达式被求值时,它会返回字符串"someone"或提供的名称
- 而且你已经知道 if 在 Kotlin 中也是一种表达式
fun main() {
val name = readln()
println("Hello, ${if (name.isBlank()) "someone" else name}!") // <1>
// Blank input: Hello, someone!
// "Seb" input: Hello, Seb!
}