学习Kotlin编程语言
Kotlin是世界各地的Android开发者广泛使用的一种编程语言。本主题可充当Kotlin速成课程,能够帮助您快速上手。
变量声明
Kotlin使用两个不同的关键字(即val和var)来声明变量。
- val用于值不可更改的变量。您不能为使用val声明的变量重新赋值。
- var用于值可以更改的变量。
在一下示例中,cout是一个Int类型的变量,为其赋予的初始值为10:
var count: Int = 10
Int是一种表示整数的类型,它是可以在Kotlin中表示的众多数值类型之一。与其他语言类似,您也可以使用Byte、Short、Long、Float和Double,具体取决于您的数值数据。
var关键字表示您可以根据需要为count重新赋值。例如,您可以将count的值从10更改为15:
var count: Int = 10
count = 15
不过,有些值不应更改。假设有一个名为languageName的String。如果您要确保languageName的值始终为“Kotlin”,则可以使用val关键字来声明languageName:
val languageName: String = "Kotlin"
通过这些关键字,您可以明确指出哪些变量的值可以更改,您可以根据需要加以利用。如果变量引用必须可重新赋值,则将其声明为var。否则,请使用val。
类型推断
接着前面的示例来讲,当您为languageName赋予初始值时,Kotlin编译器可根据所赋值的类型来推断类型。
由于“Kotlin”的值的类型为String,因此编译器推断languageName也为String。请注意,Kotlin是一种静态语言。这意味着,类型在编译时解析且从不改变。
在以下示例中,languageName推断为String,因此您无法对其调用任何不属于String类的函数:
val languageName = "Kotlin"
val upperCaseName = languageName.toUpperCase()
// Fails to compile.
languageName.inc()
toUpperCase()是一个只能对String类型的变量调用的函数。由于Kotlin编译器已将languageName推断为String,因此您可以安全地调用toUpperCase()。不过,inc()是一个Int运算符函数,因此无法对String调用它。利用Kotlin的类型推断,您既能确保代码见解,又能确保类型安全。
Null安全
在某些语言中,可以声明引用类型变量而不明确提供初始值。在这些情况下,变量通常包含null值。默认情况下,Kotlin变量不能持有null值。这意味着以下代码段无效:
// Fails to compile
val languageName: String = null
要使变量持有null值,它必须是可为null类型。您可以再变量类型后面加上?后缀,将变量指定可为null,如下示例所示:
val languageName: String? = null
指定String?类型后,您可以为languageName赋予String值或null。
您必须小心处理可为null的变量,否则可能会出现可怕的NullPointerException。例如,在Java中,如果您尝试对null值调用方法,您的程序会发生崩溃。
Kotlin提供了多种极致类安全地处理可为null的变量。如需了解详情,请参阅Android平台中常见的Kotlin模式:可为null性。
条件语句
Kotlin提供了几种用来实现条件逻辑的机制,其中最常见的是if-else语句。如果if关键字后面括在圆括号内的表达式求值为true,则会执行该分支中的代码(即,紧跟在后面的括在大括号内的代码)。否则,会执行else分支中的代码。
if (count == 42) {
println("I have the answer.")
} else {
println("The answer eludes me.")
}
您可以使用else if表示多个条件。这样,您就可以再耽搁条件语句中表示更精细且复杂的逻辑,如以下示例所示:
if (count == 42) {
println("I have the answer.")
} else if (count > 35) {
println("The answer is close.")
} else {
println("The answer eludes me.")
}
条件语句对于表示有状态的逻辑很实用,但您可能会发现,编写这些语句时会重复。在上面的示例中,就是在每个分支中输出一个String。为了避免这种重复,Kotlin提供了条件表达式。最后一个示例可以重新编写如下:
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表达式的结果为answerStrig赋予了一个初始值。可以利用类型推断来省略answerString的显示类型声明,但为了清楚起见,通常最好添加该声明。
!!! 注意:Kotlin不包含传统的三元运算符,而是倾向于使用条件表达式。
随着条件语句的复杂性不断增加,您可以考虑将if-else表达式替换为when表达式,如下所示:
val answerString = when {
count == 42 -> "I have the answer."
coutn > 35 -> "The answer is close."
else -> "The answer eludes me."
}
println(answerString)
when表达式中的每个分支都由一个条件、一个箭头(->)和一个结果来表示。如果箭头左侧的条件求值为true,则会返回右侧的表达式结果。请注意,执行并不是从一个分支跳转到下一个分支。when表达式示例中的代码在功能上与上一个示例中的代码等效,但可以说更易读。
Kotlin的条件语句彰显了它的一项更强大的功能,即智能类型转换。您不必使用安全调用运算符或非null断言运算符来处理可为null的值,而是可以使用条件语句来检查变量是否包含对null值的引用。如以下示例所示:
va languageName: String? = null
if (language != null) {
// No need to write languageName?.toUpperCase()
println(languageName.toUpperCase())
}
在条件分支中,languageName可能会被视为不可为null。Kotlin非常智能,能够明白执行分支的条件是languageName不持有null值,因此您不必在该分支中将languageName视为可为null。这种智能类型转换适用于null检查、类型检查,或符合约定的任何条件。
函数
您可以将一个或多个表达式归入一个函数。您可以将相应的表达式封装在一个函数中并调用该函数,而不必在每次需要某个结果时都重复同一系列的表达式。
要声明函数,请使用fun关键字,后跟函数名称。接下来,定义函数接受的输入类型(如果有),并声明它返回的输出类型。函数的主体用于定义在调用函数时的表达式。
以前面的示例为基础,下面给出了一个完整的Kotlin函数:
fun generateAnswerString(): String {
val answerString = if (count == 42) {
"I have the answer."
} else {
"The answer eludes me."
}
return answerString
}
上面示例中的函数名为generateAnswerString。它不接受任何输入。它会输出String类型的结果。要调用函数,请使用函数名称,后跟调用运算符(())。在下面的示例中,使用generateAnswerString()的结果对answerString变量进行了初始化。
val answerString = generateAnswerString()
函数可以接受参数输入,如以下示例所示:
fun generateAnswerString(countThreshold: Int): String {
val answerString = if (count > countThreshold) {
"I have the answer."
} else {
"The answer eludes me."
}
return answerString
}
在声明函数时,您可以指定任意数量的参数及其类型。在上面的示例中国,generateAnswerString()接受一个名为countThreshold且类型为Int的参数。在函数中,您可以使用参数的名称来引用参数。
调用此函数时,您必须在函数调用的圆括号内添加一个参数:
val answerString = generateAnswerString(42)
简化函数声明
generateAnswerString()是一个相当简单的函数。该函数声明一个变量,然后立即返回结果。从函数返回单个表达式的结果时,您可以通过直接返回函数中包含的if-else表达式的结果来跳过声明局部变量,如以下示例所示:
fun generateAnswerString(countThreshold: Int): String {
return if (count > countThreshold) {
"I have the answer."
} else {
"The answer eludes me."
}
}
您还可以将return关键字替换为赋值运算符:
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 = stringLenghtFunc("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", {inout ->
input.length
})
如果匿名函数是在某个函数上定义的最后一个参数,则您可以在用于调用该函数的圆括号之外传递它,如以下示例所示:
stringMapper("Android") { input ->
input.length
}
您可以在整个Kotlin标准库中找到匿名函数。如需了解详情,请参阅高阶函数和Lambda。
类
到目前为止提到的所有类型都内置在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>)
在上面的示例中,类构造函数将List<Wheel>当做构造函数参数,并使用该参数来初始化其wheels属性。
类函数和封装
类使用函数对行为建模。函数可以修改状态,从而帮助您只公开希望公开的数据。这种访问控制机制属于一个更大的面向对象的概念(称为“封装”)。
在一下示例中,doorLock属性对Car类外部的一切都不公开。要解锁汽车,您必须调用onlockDoor()函数并传入有效的“钥匙”,如以下示例所示:
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 = ...
val gallonsOfFuelInTank: Int = 15
private set
fun unlockDoor(key: Key): Boolean {
// Return true if key is valid for door lock, false otherwise
}
}
通过结合使用属性和函数,您可以创建能够对所有类型的对象建模的类。
互操作性
Kotlin最重要的功能之一就是它与Java之间的互操作性。由于Kotlin代码可编译为JVM字节码,因此Kotlin代码可以直接调用Java代码,反之亦然。这意味着,您可以直接从Kotlin利用现有的Java库。此外,绝大多数Android API都是用Java编写的,您可以直接从Kotlin调用它们。
Kotlin是一种灵活而实用的语言,它的支持力量和发展势头日益强劲。如果您还没有试过,我们建议您试一下。接下来,请查看Kotlin官方文档遗迹关于如何在Androd应用中运用常见Kotlin模式的指南。