变量声明
val
用于值从不更改的变量。使用val
声明的变量无法重新赋值。var
用于值可以更改的变量。
可以被重新赋值
var count: Int = 10
//不能被赋值
val languageName: String = "Kotlin"
类型推断
val languageName = "Kotlin"
val upperCaseName = languageName.toUpperCase()
// Fails to compile
languageName.inc()
toUpperCase()
是一个只能对 String
类型的变量调用的函数。由于 Kotlin 编译器已将 languageName
推断为 String
,因此可以安全地调用 toUpperCase()
。不过,inc()
是一个 Int
运算符函数,因此无法对 String
调用它。利用 Kotlin 的类型推断,既能确保代码简洁,又能确保类型安全。
Null 安全
要使变量持有 null 值,它必须是可为 null 类型。可以在变量类型后面加上 ?
后缀,将变量指定为可为 null,如以下示例所示:
val languageName: String? = null
条件语句
val answerString: String = if (count == 42) {
"I have the answer."
} else if (count > 35) {
"The answer is close."
} else {
"The answer eludes me."
}
println(answerString)
每个条件分支都隐式地返回其最后一行的表达式的结果,因此无需使用 return
关键字。由于全部三个分支的结果都是 String
类型,因此 if-else 表达式的结果也是 String
类型。在本例中,根据 if-else 表达式的结果为 answerString
赋予了一个初始值。利用类型推断可以省略 answerString
的显式类型声明,但为了清楚起见,通常最好添加该声明。
Kotlin 不包含传统的三元运算符,而是倾向于使用条件表达式。
val answerString = when {
count == 42 -> "I have the answer."
count > 35 -> "The answer is close."
else -> "The answer eludes me."
}
println(answerString)
when
表达式中每个分支都由一个条件、一个箭头 (->
) 和一个结果来表示。如果箭头左侧的条件求值为 true,则会返回右侧的表达式结果。请注意,执行并不是从一个分支跳转到下一个分支。when
表达式示例中的代码在功能上与上一个示例中的代码等效,但可能更易懂
函数
您可以将一个或多个表达式归入一个函数。您可以将相应的表达式封装在一个函数中并调用该函数,而不必在每次需要某个结果时都重复同一系列的表达式。
要声明函数,请使用 fun
关键字,后跟函数名称。接下来,定义函数接受的输入类型(如果有),并声明它返回的输出类型。函数的主体用于定义在调用函数时调用的表达式。
fun generateAnswerString(): String {
val answerString = if (count == 42) {
"I have the answer."
} else {
"The answer eludes me"
}
return answerString
}
上面示例中的函数名为 generateAnswerString
。它不接受任何输入。它会输出 String
类型的结果。要调用函数,请使用函数名称,后跟调用运算符 (()
)。在下面的示例中,使用 generateAnswerString()
的结果对 answerString
变量进行了初始化。
函数可以接受参数输入,如以下示例所示:
fun generateAnswerString(countThreshold: Int): String {
val answerString = if (count > countThreshold) {
"I have the answer."
} else {
"The answer eludes me."
}
return answerString
}
简化函数声明
fun generateAnswerString(countThreshold: Int): String {
return if (count > countThreshold) {
"I have the answer."
} else {
"The answer eludes me."
}
}
fun generateAnswerString(countThreshold: Int): String = if (count > countThreshold) {
"I have the answer"
} else {
"The answer eludes me"
}
匿名函数
并非每个函数都需要一个名称。某些函数通过输入和输出更直接地进行标识。这些函数称为“匿名函数”。您可以保留对某个匿名函数的引用,以便日后使用此引用来调用该匿名函数。与其他引用类型一样,您也可以在应用中传递引用。
val stringLengthFunc: (String) -> Int = { input ->
input.length
}
与命名函数一样,匿名函数也可以包含任意数量的表达式。 函数的返回值是最终表达式的结果。
在上面的示例中,stringLengthFunc 包含对一个匿名函数的引用,该函数将 String 当作输入,并将输入 String 的长度作为 Int 类型的输出返回。因此,该函数的类型表示为 (String) -> Int。不过,此代码不会调用该函数。要检索该函数的结果,您必须像调用命名函数一样调用该函数。调用 stringLengthFunc 时,必须提供 String,如以下示例所示:
val stringLengthFunc: (String) -> Int = { input ->
input.length
}
val stringLength: Int = stringLengthFunc("Android")
高阶函数
一个函数可以将另一个函数当作参数。将其他函数用作参数的函数称为“高阶函数”。此模式对组件之间的通信(其方式与在 Java 中使用回调接口相同)很有用
fun stringMapper(str: String, mapper: (String) -> Int): Int {
// Invoke function
return mapper(str)
}
stringMapper()
函数接受一个 String
以及一个函数,该函数根据您传递给它的 String
来推导 Int
值。
要调用 stringMapper()
,可以传递一个 String
和一个满足其他输入参数的函数(即,一个将 String
当作输入并输出 Int
的函数),如以下示例所示:
stringMapper("Android", { input ->
input.length
})
//如果匿名函数是在某个函数上定义的最后一个参数,
//则您可以在用于调用该函数的圆括号之外传递它,如以下示例所示:
stringMapper("Android") { input ->
input.length
}
类
到目前为止提到的所有类型都已内置在 Kotlin 编程语言中。如果您希望添加自己的自定义类型,可以使用 class
关键字来定义类,如以下示例所示:
class Car
类属性
类使用属性来表示状态。属性是类级变量,可以包含 getter、setter 和后备字段。由于汽车需要轮子来驱动,因此您可以添加 Wheel
对象的列表作为 Car
的属性,如以下示例所示:
class Car {
val wheels = listOf<Wheel>()
}
请注意,wheels
是一个 public val
,这意味着,可以从 Car
类外部访问 wheels
,并且不能为其重新赋值。如果要获取 Car
的实例,必须先调用其构造函数。这样,您便可以访问它的任何可访问属性。
val car = Car() // construct a Car
val wheels = car.wheels // retrieve the wheels value from the Car
//如果希望自定义轮子,可以定义一个自定义构造函数,用来指定如何初始化类属性:
class Car(val wheels: List<Wheel>)
类函数和封装
类使用函数对行为建模。函数可以修改状态,从而帮助您只公开希望公开的数据。这种访问控制机制属于一个面向对象的更大概念,称为“封装”。
在以下示例中,doorLock
属性对 Car
类外部的一切都不公开。要解锁汽车,必须调用 unlockDoor()
函数并传入有效的“钥匙”,如以下示例所示:
class Car(val wheels: List<Wheel>) {
private val doorLock: DoorLock = ...
fun unlockDoor(key: Key): Boolean {
// Return true if key is valid for door lock, false otherwise
}
}
如果希望自定义属性的引用方式,则可以提供自定义的 getter 和 setter。例如,如果希望公开属性的 getter 而限制访问其 setter,则可以将该 setter 指定为 private
:
class Car(val wheels: List<Wheel>) {
private val doorLock: DoorLock = ...
var gallonsOfFuelInTank: Int = 15
private set
fun unlockDoor(key: Key): Boolean {
// Return true if key is valid for door lock, false otherwise
}
}
通过结合使用属性和函数,可以创建能够对所有类型的对象建模的类。