变量

在Kotlin中,变量分有val(value)和var(variable)修饰的变量类型,由于Kotlin有类型推导机制,因此可以不声明变量数据类型。

1
2
3
4
5
val a = 10
// val变量不可变
var b = 10
// var变量可变
b = 20

当然我们也可以显性地声明变量类型:

1
val a: Int = 10

和Java不同的是,在Kotlin中每个变量类型都是类,因此变量类型都是开头大写的

image-20240824140619262

函数

函数体如下所示:

1
2
3
fun methodName(param1: Int, param2: Int): Int { 
return 0
}

首先fun(function的简写)是定义函数的关键字,无论你 定义什么函数,都一定要使用fun来声明。

紧跟在fun后面的是函数名,这个就没有什么要求了,你可以根据自己的喜好起任何名字,但是 良好的编程习惯是函数名最好要有一定的意义,能表达这个函数的作用是什么。

函数名后面紧跟着一对括号,里面可以声明该函数接收什么参数,参数的数量可以是任意多 个,例如上述示例就表示该函数接收两个Int类型的参数。参数的声明格式是“参数名: 参数类 型”,其中参数名也是可以随便定义的,这一点和函数名类似。如果不想接收任何参数,那么写 一对空括号就可以了。

参数括号后面的那部分是可选的,用于声明该函数会返回什么类型的数据,上述示例就表示该 函数会返回一个Int类型的数据。如果你的函数不需要返回任何数据,这部分可以直接不写。

最后两个大括号之间的内容就是函数体了,我们可以在这里编写一个函数的具体逻辑。由于上 述示例中声明了该函数会返回一个Int类型的数据,因此在函数体中我们简单地返回了一个0。 这就是定义一个函数最标准的方式了

函数语法糖

如果函数只有一行代码时,我们可以不必写函数体:

1
fun largerNumber(num1: Int, num2: Int): Int = max(num1, num2)

我们也可以借助Kotlin的类型推导机制再简化

1
fun largerNumber(num1: Int, num2: Int) = max(num1, num2)

逻辑控制

if条件语句

标准型

1
2
3
4
5
6
7
8
9
fun largerNumber(num1: Int, num2: Int): Int { 
var value = 0
if (num1 > num2) {
value = num1
} else {
value = num2
}
return value
}

语法糖

我们可以直接用if语句给value赋值

1
2
3
4
5
6
7
8
fun largerNumber(num1: Int, num2: Int): Int {
val value = if(num1 > num2) {
num1
} else {
num2
}
return value
}

当然我们也可以直接return出去

1
2
3
4
5
6
7
fun largerNumber(num1: Int, num2: Int): Int {
return if(num1 > num2) {
num1
} else {
num2
}
}

我们发现只有一个return语句,因此可以继续化简:

1
2
3
4
5
6
7
8
9
fun largerNumber(num1: Int, num2: Int): Int = if(num1 > num2) {
num1
} else {
num2
}

// 或者

fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) num1 else num2

when条件语句

标准式

匹配值 -> { 执行逻辑 }

当你的执行逻辑只有一行代码时,{ }可以省略。

1
2
3
4
5
fun getScore(name: String) = when(name) {
"Tom" -> 10
"Lip" -> 20
else -> 0
}

类型匹配

1
2
3
4
5
6
7
fun checkNumber(num: Number) {
when (num) {
is Int -> println("number is Int")
is Double -> println("number is Double")
else -> println("number not support")
}
}

特殊场景

1
2
3
4
5
6
7
fun getScore(name: String) = when { 
name.startsWith("Tom") -> 86
name == "Jim" -> 77
name == "Jack" -> 95
name == "Lily" -> 100
else -> 0
}

循环语句

while循环没区别不说了

区间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
val range = 0..10
// 表示[0, 10]左闭右闭

val range2 = 0 until 10
// 表示[0, 10)左闭右开

fun main() {
for (i in range) {
println(i)
}
// 结果是0,...,10

for (i in 0 until 10 step 2) {
println(i)
}
// 结果是0, 2, 4, 6, 8

for (i in 10 downTo 1) {
println(i)
}
// 结果是10, 9, 8,...,1
}

面对对象编程

类与对象

和java没什么区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person { 
var name = ""
var age = 0
fun eat() {
println(name + " is eating. He is " + age + " years old.")
}
}

fun main() {
val p = Person()
p.name = "Jack"
p.age = 19
p.eat()
}

继承与构造函数

继承

1
2
3
4
class Student { 
var sno = ""
var grade = 0
}

我们需要注意的是,Kotlin中默认类是不允许继承的!

我们需要加上open关键字:

1
open class Person { ... }

这样子Person类才可以被继承

第二件事,要让Student类继承Person类。在Java中继承的关键字是extends,而在Kotlin 中变成了一个冒号,写法如下:

1
2
3
4
class Student : Person() { 
var sno = ""
var grade = 0
}

可见性修饰

image-20240824144213427

构造函数

1
2
3
4
5
6
7
8
9
class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age) {
init{
println("主构造逻辑")
}
constructor(name: String, age: Int) : this("", 0, name, age) {
}
constructor() : this("", 0) {
}
}

Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age)是主构造函数,init为可选的主构造函数,可以在里面添加逻辑

其中的constructor均为次构造函数

任何一个类只能有一个主构造函数,但是可以有多个次构造函数。次构造函数也可 以用于实例化一个类,这一点和主构造函数没有什么不同,只不过它是有函数体的。

当然我们可以直接用参数默认值简化:

1
class Student(val sno: String = "", val grade: Int = 0, name: String = "", age: Int = 0) : Person(name, age) { }

这样子就不需要次构造函数了

接口

接口可以不需要函数体,若没有函数体,继承的类需要覆盖;若有,继承的类可以不覆盖

接口的继承不需要括号

1
2
3
4
interface Study { 
fun readBooks()
fun doHomework()
}
1
2
3
4
5
6
7
8
class Student(name: String, age: Int) : Person(name, age), Study { 
override fun readBooks() {
println(name + " is reading.")
}
override fun doHomework() {
println(name + " is doing homework.")
}
}

数据类与单例类

数据类

数据类通常需要重写equals()、hashCode()、toString()这几个方法。其中,equals() 方法用于判断两个数据类是否相等。hashCode()方法作为equals()的配套方法,也需要一起 重写,否则会导致HashMap、HashSet等hash相关的系统类无法正常工作。toString()方法 用于提供更清晰的输入日志,否则一个数据类默认打印出来的就是一行内存地址。

New→Kotlin File/Class,在弹出的对话框中输 入“Cellphone”,创建类型选择“Class”。然后在创建的类中编写如下代码:

1
data class Cellphone(val brand: String, val price: Double)

当在一个类前 面声明了data关键字时,就表明你希望这个类是一个数据类,Kotlin会根据主构造函数中的参 数帮你将equals()、hashCode()、toString()等固定且无实际逻辑意义的方法自动生成, 从而大大减少了开发的工作量。

单例类

java中的单例模式:

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton { 
private static Singleton instance;
private Singleton() {}
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance; }
public void singletonTest() {
System.out.println("singletonTest is called.");
}
}

而如果我们想调用单例类中的方法,也很简单,比如想调用上述的singletonTest()方法, 就可以这样写:

1
2
Singleton singleton = Singleton.getInstance(); 
singleton.singletonTest();

在Kotlin中创建一个单例类的方式极其简单,只需要将class关键字改成object关键字即可。 现在我们尝试创建一个Kotlin版的Singleton单例类,右击com.example.xxx包 →New→Kotlin File/Class,在弹出的对话框中输入“Singleton”,创建类型选择“Object”,点 击“OK”完成创建,初始代码如下所示:

1
object Singleton { }

现在Singleton就已经是一个单例类了,我们可以直接在这个类中编写需要的函数,比如加入 一个singletonTest()函数:

1
2
3
4
5
object Singleton { 
fun singletonTest() {
println("singletonTest is called.")
}
}

如果我们想调用直接:

1
Singleton.singletonTest()

这种写法虽然看上去像是静态方法的调用,但其实Kotlin在背后自动帮我们创建了一个 Singleton类的实例,并且保证全局只会存在一个Singleton实例。

Lambda编程

集合API

列表

1
2
3
4
5
6
val list = ArrayList<String>() 
list.add("Apple")
list.add("Banana")
list.add("Orange")
list.add("Pear")
list.add("Grape")

我们可以直接用

1
2
3
4
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")

val list2 = mutableListOf("Apple", "Banana", "Orange", "Pear", "Grape")
list2.add("Watermelon")

Map集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
val map = HashMap<String, Int>()
map.put("Apple", 1)
map.put("Banana", 2)
map.put("Orange", 3)
map.put("Pear", 4)
map.put("Grape", 5)

val map2 = HashMap<String, Int>()
map2["Apple"] = 1
map2["Banana"] = 2
map2["Orange"] = 3
map2["Pear"] = 4
map2["Grape"] = 5

val map3 = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4, "Grape" to 5)

for ((fruit, number) in map3) {
println("fruit is " + fruit + ", number is " + number)
}

集合的函数式API

maxby

1
2
3
4
5
6
7
8
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon") 
var maxLengthFruit = ""
for (fruit in list) {
if (fruit.length > maxLengthFruit.length) {
maxLengthFruit = fruit
}
}
println("max length fruit is " + maxLengthFruit)

我们可以用下面的来改写:

1
2
3
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon") 
val maxLengthFruit = list.maxBy { it.length }
println("max length fruit is " + maxLengthFruit)

我们来看一下Lambda表达式的语法结构:

{参数名1: 参数类型, 参数名2: 参数类型 -> 函数体}

因此上面代码的lambda表示其实是:

1
2
3
4
5
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon") 
//val lambda = { fruit: String -> fruit.length }
//val maxLengthFruit = list.maxBy(lambda)

val maxLengthFruit = list.maxBy({ fruit: String -> fruit.length })

然后Kotlin规定,当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到函数括 号的外面,且如果Lambda参数是函数的唯一一个参数的话,还可以将函数的括号省略,同时Lambda表达式中的参数列表其实在大多数情况下不必声明参数类型,最后,当Lambda表达式的参数列表中只有一个参数时,也不必声明参数名,而是可以使用it 关键字来代替。因此:

1
2
3
4
5
6
7
8
9
10
11
//当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到函数括 号的外面
val maxLengthFruit = list.maxBy() { fruit: String -> fruit.length }

//如果Lambda参数是函数的唯一一个参数的话,还可以将函数的括号省略
val maxLengthFruit = list.maxBy { fruit: String -> fruit.length }

// Lambda表达式中的参数列表其实在大多数情况下不必声明参数类型
val maxLengthFruit = list.maxBy { fruit -> fruit.length }

//当Lambda表达式的参数列表中只有一个参数时,也不必声明参数名
val maxLengthFruit = list.maxBy { it.length }

map

1
2
3
4
5
6
7
fun main() { 
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val newList = list.map { it.toUpperCase() }
for (fruit in newList) {
println(fruit)
}
}

filter

1
2
3
4
5
6
7
8
fun main() { 
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val newList = list.filter { it.length <= 5 } .map { it.toUpperCase() }
for (fruit in newList) {
println(fruit)
}
}
//先filter再map

any和all

1
2
3
4
5
6
fun main() { 
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val anyResult = list.any { it.length <= 5 }
val allResult = list.all { it.length <= 5 }
println("anyResult is " + anyResult + ", allResult is " + allResult) }
//true, false