Kotlin的语法入门

前言

Kotlin 是一种现代化的静态类型编程语言,最初由 JetBrains 开发并于 2011 年公开发布。它是一种通用编程语言,设计用于与 Java 100% 兼容,因此可以在 Java 平台上无缝运行。Kotlin 专注于简洁性、可读性和可维护性,并且旨在减少代码的样板和冗余性。

Kotlin 具有许多吸引人的特性,包括:

  1. 与 Java 互操作性: Kotlin 可以与 Java 代码完全互操作,这意味着现有的 Java 项目可以逐步采用 Kotlin,而不需要重新编写整个代码库。

  2. 空安全: Kotlin 在语言级别支持空安全,这意味着它能够防止常见的空指针异常,并提供更安全的代码。

  3. 扩展函数: Kotlin 允许开发者在不修改类定义的情况下向现有类添加新方法,这通过所谓的扩展函数实现。

  4. 函数式编程支持: Kotlin 提供了许多函数式编程的特性,如 Lambda 表达式、高阶函数等,使得编写函数式风格的代码更加容易。

  5. 协程: Kotlin 通过协程提供了一种轻量级的并发编程方式,能够简化异步代码的编写,提高代码的可读性和可维护性。

  6. 数据类: Kotlin 提供了数据类(data class)的概念,简化了用于数据传输和持久化的类的编写。

  7. 可扩展性: Kotlin 允许开发者通过编写自定义的 DSL(领域特定语言)来扩展语言的功能,从而使其适应特定领域的需求。

总的来说,Kotlin 是一种强大而现代的编程语言,适用于各种应用场景,从 Android 移动应用开发到企业级后端服务开发。

其简洁性、安全性和与 Java 的无缝集成使得它成为了许多开发者的首选之一。

那么性能呢?

Kotlin 和 Java 在性能方面没有明显的优劣之分,它们在 JVM 上都可以实现高性能的应用程序。

这是因为 Kotlin 与 Java 共享 JVM 运行环境,并且 Kotlin 编译器会将 Kotlin 代码编译成与 Java 字节码兼容的字节码。

数据类型

在 Kotlin 中,数据类型包括基本数据类型和引用数据类型。下面是 Kotlin 中常见的数据类型:

基本数据类型

  • 整数类型
    • Byte:8 位有符号整数,取值范围为 -128 到 127。
    • Short:16 位有符号整数,取值范围为 -32768 到 32767。
    • Int:32 位有符号整数,取值范围为 -2^31 到 2^31 - 1。
    • Long:64 位有符号整数,取值范围为 -2^63 到 2^63 - 1。
  • 浮点类型
    • Float:32 位单精度浮点数。
    • Double:64 位双精度浮点数。
  • 其他基本类型
    • Char:16 位 Unicode 字符。
    • Boolean:布尔类型,取值为 truefalse

引用数据类型

  • 字符串:使用 String 类表示,是不可变的。
  • 数组:Kotlin 中的数组有固定大小,使用 Array 类表示。
  • 集合:Kotlin 标准库提供了丰富的集合类,如 ListSetMap 等。
  • :开发者可以定义自己的类来表示复杂的数据结构。

Kotlin 还支持类型推断,这意味着在大多数情况下,编译器可以推断出变量的类型,而不需要显式地指定类型。

例如:

1
2
val number = 42 // Kotlin 编译器会推断出 number 的类型为 Int
val name = "Kotlin" // Kotlin 编译器会推断出 name 的类型为 String

字符串

下面的 Kotlin 代码示例,展示了如何使用字符串模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fun main() {
val name = "Alice"
val age = 30
val city = "Wonderland"

// 使用字符串模板输出信息
println("姓名:$name,年龄:$age,城市:$city")

// 字符串模板中可以包含表达式
println("明年${age + 1}岁时,$name 将在 $city 庆祝生日。")

// 使用字符串模板作为函数参数
greet("$name", age)
}

fun greet(name: String, age: Int) {
println("你好,$name!你今年$age岁了。")
}

在上面的示例中,我们定义了变量 nameagecity,然后使用字符串模板将它们嵌入到字符串中。

在第二个 println 语句中,我们还展示了如何在字符串模板中包含表达式。

最后,我们调用了 greet 函数,并将字符串模板作为参数传递给函数。

数组操作

在Kotlin中,数组的操作可以通过标准库中的Array类来完成。

以下是一些常见的 Kotlin 数组操作:

创建数组

使用构造函数创建数组

1
val array = Array(5) { i -> i * 2 } // 创建一个包含 5 个元素的数组,每个元素的值是索引的两倍

使用arrayOf函数创建数组

1
val array = arrayOf(1, 2, 3, 4, 5) // 创建一个包含指定元素的数组

访问数组元素

1
val element = array[index] // 获取数组中指定索引处的元素值

修改数组元素

1
array[index] = value // 修改数组中指定索引处的元素值

数组切片

1
val subArray = array.sliceArray(1..3) // 返回数组中指定范围的子数组

数组合并

1
val combinedArray = array1 + array2 // 将两个数组合并为一个新数组

遍历数组

使用for循环

1
2
3
for (element in array) {
// 对每个元素执行操作
}

使用forEach高阶函数

1
2
3
array.forEach { element ->
// 对每个元素执行操作
}

数组转换

1
val newArray = array.map { it * 2 } // 将数组中的每个元素乘以 2 并返回一个新的数组

过滤数组

1
val filteredArray = array.filter { it % 2 == 0 } // 过滤数组中的偶数元素

查找数组元素

1
val index = array.indexOf(element) // 返回指定元素在数组中的索引,如果不存在则返回 -1

数组排序

1
2
3
val sortedArray = array.sorted() // 返回一个排序后的新数组

array.sort() // 原地对数组进行排序

集合类

Set/List/Map

首先要说的是没有new

集合的分类:

  • Set(集)
  • List(列表)
  • Map(映射)

Kotlin中,明确的区分了只读可变的集合
代码如下 前三个是只读 后三个是可变的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var list = listOf<String>("aa", "bb", "cc")
list.forEach { item ->
Log.i("Kotlin", item)
}

var set = setOf<String>("aa", "bb", "cc")
set.forEach { item ->
Log.i("Kotlin", item)
}


val map = mapOf("a" to 1, "b" to 2, "c" to 3)
var map = mapOf<String, String>(Pair("aa", "AA"), Pair("bb", "BB"), Pair("cc", "CC"));
for ((key,value) in map){
Log.i("Kotlin", "key:$key value:$value")
}

可变长度

1
2
3
4
5
6
7
8
9
10
11
var mList = mutableListOf<String>();
mList.add("aa")
Log.i("Kotlin", mList[0])

var mSet = mutableSetOf<String>();
mSet.add("aa")
Log.i("Kotlin", mSet.elementAt(0))

var mMap = mutableMapOf<String, String>()
mMap["aa"] = "AA"
Log.i("Kotlin", "$mMap")

List处理

遍历

只遍历项

1
2
3
4
var list = listOf<String>("aa", "bb", "cc")
list.forEach { item ->
Log.i("Kotlin", item)
}

遍历索引和项

有几种方法可以做到这一点,以下是其中的一些:

使用 forEachIndexed 函数:

1
2
3
4
val list = listOf("a", "b", "c")
list.forEachIndexed { index, item ->
println("索引 $index 对应的项为 $item")
}

使用 indicesget 属性:

1
2
3
4
val list = listOf("a", "b", "c")
for (i in list.indices) {
println("索引 $i 对应的项为 ${list[i]}")
}

使用 withIndex() 函数:

1
2
3
4
val list = listOf("a", "b", "c")
for ((index, item) in list.withIndex()) {
println("索引 $index 对应的项为 $item")
}

以上方法都可以用来遍历 List,并在每次迭代中获取索引和项。

选择哪种方法取决于个人偏好和代码风格。

map

1
2
3
val ulist = mutableListOf<String>("A", "B", "C")
val ulistNew = ulist.map { c -> c.toLowerCase(Locale.ROOT) }
L.e("ulistNew:${ulistNew}")

结果

ulistNew:[a, b, c]

filter

1
2
3
4
5
val ulist = mutableListOf<Int>(1, 2, 3)
val ulistNew = ulist.filter {
it >= 2
}
L.e("ulistNew:${ulistNew}")

结果

ulistNew:[2, 3]

foreach

1
2
3
4
val ulist = mutableListOf<Int>(1, 2, 3)
ulist.forEach{
L.e("ulistNew:${it}")
}

结果

1
2
3

可变长参数函数

函数的变长参数可以用 vararg 关键字进行标识

1
2
3
4
5
6
7
8
9
10
fun varargFun(vararg v:String){
for(vt in v){
print(vt)
}
}
//普通传递:
varargFun(1,"aaa","bbb","ccc","ddd","fff")
//数组传递:
val strArray = arrayOf("aaa","bbb","ccc","ddd","fff")
varargFun(1,*strArray)

表达式

三目运算

1
if(a>b) a else b

When 表达式

when 取代了 C 风格语言的 switch

也能替换if/else

1
2
3
4
5
6
7
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // Note the block
print("x is neither 1 nor 2")
}
}

循环

1
2
3
4
5
for (i in 1..4) print(i) //打印1234
for (i in 1 until 4)print(i) //打印123
for (i in 4 downTo 1 step 1) print(i) //打印4321
for (i in (1..4).reversed()) print(i) // prints "4321"
for (i in (1..4).reversed() step 2) print(i) // prints "42"

循环对象带索引

1
2
3
4
5
6
val list = listOf("苹果", "香蕉", "橙子", "葡萄")

// 循环带索引的元素
for ((index, item) in list.withIndex()) {
println("索引: $index, 元素: $item")
}

异常处理

在 Kotlin 中,异常处理与 Java 类似,但有一些语法上的差异和特点。

下面是 Kotlin 中异常处理的一般方法:

try-catch 块:

Kotlin 使用 trycatch 关键字来捕获异常。

与 Java 不同的是,在 Kotlin 中,catch 子句被看作是一个表达式,因此它可以返回一个值。这使得异常处理更加灵活。

示例如下:

1
2
3
4
5
6
7
try {
// 可能会抛出异常的代码
} catch (e: SomeException) {
// 处理异常的代码
} finally {
// 可选的 finally 块
}

try 表达式:

Kotlin 还提供了 try 表达式,它类似于 Java 中的 try-catch-finally 结构,但它可以作为表达式来使用,因此可以直接返回一个值。

示例如下:

1
2
3
4
5
6
val result = try {
// 可能会抛出异常的代码
} catch (e: SomeException) {
// 处理异常的代码
defaultValue // 指定一个默认值
}

Throw 表达式:

在 Kotlin 中,可以使用 throw 关键字抛出异常。与 Java 类似,你可以抛出任何类型的异常。示例如下:

1
2
3
4
5
6
fun divide(a: Int, b: Int): Int {
if (b == 0) {
throw IllegalArgumentException("Divider cannot be zero")
}
return a / b
}

自定义异常类:

与 Java 一样,你可以在 Kotlin 中创建自定义异常类。示例如下:

1
class CustomException(message: String) : Exception(message)

不检查异常(Unchecked Exceptions):

Kotlin 不区分受检查异常和不受检查异常,因此不需要在函数声明中声明可能抛出的异常,但你仍然可以选择捕获或抛出异常。

总的来说,Kotlin 的异常处理与 Java 相似,但具有一些语法上的差异和特点,使得异常处理更加灵活和方便。

日期处理

以下是一个使用Kotlin处理日期的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.time.LocalDate
import java.time.format.DateTimeFormatter

fun main() {
// 获取当前日期
val currentDate = LocalDate.now()
println("当前日期: $currentDate")

// 格式化日期
val formattedDate = currentDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
println("格式化后的日期: $formattedDate")

// 解析字符串为日期
val dateStr = "2024-03-17"
val parsedDate = LocalDate.parse(dateStr)
println("解析后的日期: $parsedDate")

// 比较日期
val futureDate = LocalDate.of(2025, 3, 17)
if (futureDate.isAfter(currentDate)) {
println("$futureDate 在未来")
} else if (futureDate.isBefore(currentDate)) {
println("$futureDate 在过去")
} else {
println("$futureDate 是今天")
}

// 添加或减去天数
val modifiedDate = currentDate.plusDays(5)
println("添加5天后的日期: $modifiedDate")

val modifiedDate2 = currentDate.minusMonths(2)
println("减去2个月后的日期: $modifiedDate2")
}

这个示例演示了如何获取当前日期,格式化日期,解析字符串为日期,比较日期,以及对日期进行简单的算术操作,如添加或减去天数和月份。