一、为什么要学这个?
-
更简洁的代码:Kotlin 可以减少代码量,并提高代码的可读性和可维护性。Kotlin 通过简化常见的编程任务来提高开发效率。
-
更安全的代码:Kotlin 的类型系统可以在编译时捕获许多常见的编程错误,从而减少运行时错误的数量。
-
更强大的功能:Kotlin 支持许多现代编程语言的功能,如函数式编程、扩展函数、协程等,这些功能可以提高开发效率,并帮助开发人员编写更优雅的代码。
-
更好的互操作性:Kotlin 可以与 Java 代码无缝互操作,这意味着你可以逐步将 Java 代码转换为 Kotlin,而不必从头开始重写整个应用程序。
-
更好的 Android 支持:Kotlin 可以帮助开发人员编写更简洁、更清晰的 Android 代码,并且可以与 Android Studio 集成,以提高开发效率。
as集成 Kotlin
- 需要先在项目 app 模块下的
build.gradle
文件中加入配置
apply plugin: 'kotlin-android'
android {
kotlinOptions {
jvmTarget = '1.8'
}
}
- 然后在项目根目录下的
build.gradle
文件中加入 Kotlin 插件配置
buildscript {
dependencies {
// Kotlin 插件:https://plugins.jetbrains.com/plugin/6954-kotlin
// noinspection GradleDependency
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31'
}
}
学习Kotlin 编程语言
变量声明
Kotlin 使用两个不同的关键字(即 val
和 var
)来声明变量。
val
用于值从不更改的变量。使用val
声明的变量无法重新赋值。其实就是常量。var
用于值可以更改的变量。
在以下示例中,count
是一个 Int
类型的变量,初始赋值为 10
:
var num : Int =10
Int
是一种表示整数的类型,是可以用 Kotlin 表示的众多数值类型之一。与其他语言类似,您还可以使用 Byte
、Short
、Long
、Float
和 Double
,具体取决于您的数值数据。
var
关键字表示可以根据需要为 num重新赋值。例如,可以将 num的值从 10
更改为 15
:
var num : Int = 10
num=15
不过,有些值不应更改。假设有一个名为 string
的 String
。如果希望确保 string
的值始终为“你好世界”,则可以使用 val
关键字声明,这个就是常量
val string : String ="你好世界"
通过这些关键字,您可以明确指出哪些变量的值可以更改。请根据需要加以利用。如果引用的变量必须可重新赋值,则将其声明为 var
。否则,请使用 val
。
类型推断
接着前面的示例来讲,为 string
赋予初始值后,Kotlin 编译器可根据所赋值的类型来推断其类型。
由于 "你好世界"
的值为 String
类型,因此编译器推断 string也为 String
。请注意,Kotlin 是一种静态类型的语言。这意味着,类型将在编译时解析且从不改变。
在以下示例中,string推断为 String
,因此无法对其调用任何不属于 String
类的函数:
val string = "love"
//转换大写
val upperCaseName =string.toUpperCase()
// inc()是整形调用的方法 所以报错
string.inc()
toUpperCase()
是一个只能对 String
类型的变量调用的函数。由于 Kotlin 编译器已将 string推断为 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 (num== 42) {
println("I have the answer.")
} else {
println("The answer eludes me.")
}
您可以使用 else if
表示多个条件。这样,您就可以在单个条件语句中表示更精细、更复杂的逻辑,如以下示例所示:
if (num == 42) {
println("I have the answer.")
} else if (num > 35) {
println("The answer is close.")
} else {
println("The answer eludes me.")
}
条件语句对于表示有状态的逻辑很有用,但您可能会发现,编写这些语句时会出现重复。在上面的示例中,每个分支都是输出一个 String
。为了避免这种重复,Kotlin 提供了条件表达式。最后一个示例可以重新编写如下:
val answer:String =if(num>30){
"1234"
}else if (num>40){
"5678"
}
每个条件分支都隐式地返回其最后一行的表达式的结果,因此无需使用 return
关键字。由于全部分支的结果都是 String
类型,因此 if-else 表达式的结果也是 String
类型。在本例中,根据 if-else 表达式的结果为 answer
赋予了一个初始值。利用类型推断可以省略 answerString
的显式类型声明,但为了清楚起见,通常最好添加该声明。
注意:Kotlin 不包含传统的三元运算符,而是倾向于使用条件表达式。
随着条件语句的复杂性不断增加,您可以考虑将 if-else 表达式替换为 when 表达式,如以下示例所示:
var num : Int = 10
val answer : String = when{
num == 100 -> "yes"
num > 100 -> "no"
else -> "not yes not no"
}
when
表达式中每个分支都由一个条件、一个箭头 (->
) 和一个结果来表示。如果箭头左侧的条件求值为 true,则会返回右侧的表达式结果。请注意,执行并不是从一个分支跳转到下一个分支。when
表达式示例中的代码在功能上与上一个示例中的代码等效,但可能更易懂。
Kotlin 的条件语句彰显了它的一项更强大的功能,即智能类型转换。您不必使用安全调用运算符或非 null 断言运算符来处理可为 null 的值,而可以使用条件语句来检查变量是否包含对 null 值的引用,如以下示例所示:
val languageName: String? = null
if (languageName != null) {
// No need to write languageName?.toUpperCase()
println(languageName.toUpperCase())
}
在条件分支中,languageName
可能会被视为不可为 null。Kotlin 非常智能,能够识别执行分支的条件是 languageName
不持有 null 值,因此您不必在该分支中将 languageName
视为可为 null。这种智能类型转换适用于 null 检查、类型检查,或符合合约的 条件。
函数
您可以将一个或多个表达式归入一个函数。您可以将相应的表达式封装在一个函数中并调用该函数,而不必在每次需要某个结果时都重复同一系列的表达式。
要声明函数,请使用 fun
关键字,后跟函数名称。接下来,定义函数接受的输入类型(如果有),并声明它返回的输出类型。函数的主体用于定义在调用函数时调用的表达式。
以前面的示例为基础,下面给出了一个完整的 Kotlin 函数:
fun getInt(): Int {
return 1 + 1
}
上面示例中的函数名为 getInt。它不接受任何输入。它会输出 int类型的结果。要调用函数,请使用函数名称,后跟调用运算符 (()
)。在下面的示例中,使用 getInt()
的结果对 answer
变量进行了初始化。
val answer = getInt()
函数可以接受参数输入,如以下示例所示:
//带参数函数
fun getIntForNum(num:Int) : Int{
val answer = if (num > 1) {
1
} else {
0
}
return answer
}
在声明函数时,可以指定任意数量的参数及其类型。在上面的示例中,getIntForNum()
接受一个名为 num的 Int
类型的参数。在函数中,可以使用参数的名称来引用参数。
调用此函数时,必须在函数调用的圆括号内添加一个参数:
val answerString = getIntForNum(42)
简化函数声明
getIntForNum()
是一个相当简单的函数。该函数声明一个变量,然后立即返回结果。函数返回单个表达式的结果时,可以通过直接返回函数中包含的 if-else 表达式的结果来跳过声明局部变量,如以下示例所示:
fun getIntForNum(countThreshold: Int): Int{
return if (10> countThreshold) {
0
} else {
1
}
}
您还可以将 return 关键字替换为赋值运算符:
fun getAnswer(countThreshold: Int): String = if (10 > countThreshold) {
"I have the answer"
} else {
"The answer eludes me"
}
匿名函数
并非每个函数都需要一个名称。某些函数通过输入和输出更直接地进行标识。这些函数称为“匿名函数”。您可以保留对某个匿名函数的引用,以便日后使用此引用来调用该匿名函数。与其他引用类型一样,您也可以在应用中传递引用。
//匿名函数 这个变量为 一个匿名函数赋值的一个变量 一个 输入参数为STring类型 返回Int类型的变量
val getStringLength: (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 getStringLength(string : String, getLength: (String) -> Int): Int {
return getLength(string)
}
//下面是上述的geLength匿名函数
private val getLength: (String) -> Int ={input ->
input.length
}
//调用高阶函数
fun userHightFun() {
val stringLength: Int = getStringLength("aaaa", getLength)
}
getStringLength()
函数接受一个 String
以及一个函数,该函数根据您传递给它的 String
来推导 Int
值。
要调用 getStringLength()
,可以传递一个 String
和一个满足其他输入参数的函数(即,一个将 String
当作输入并输出 Int
的函数),如以下示例所示:
stringMapper("Android", { input ->
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
类外部的一切都不公开。要解锁汽车,必须调用 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
}
}
通过结合使用属性和函数,可以创建能够对所有类型的对象建模的类。
互操作性
Kotlin 最重要的功能之一就是它与 Java 之间流畅的互操作性。由于 Kotlin 代码可编译为 JVM 字节码,因此 Kotlin 代码可直接调用 Java 代码,反之亦然。这意味着,您可以直接从 Kotlin 利用现有的 Java 库。此外,绝大多数 Android API 都是用 Java 编写的,因此可以直接从 Kotlin 调用它们。
后续措施
Kotlin 是一种灵活而实用的语言,它的支持力量和发展势头日益强劲。如果您还没有尝试过,我们建议您试一下。接下来,请查看 Kotlin 官方文档以及关于如何在 Android 应用中应用常见 Kotlin 模式的指南。 。