Kotlin语言学习

注意
本文最后更新于 2024-01-22,文中内容可能已过时。

原文链接:https://ovea-y.cn/kotlin_lang_simple_learn/

背景

从2017年开始,Google在2017年的I/O大会上宣布,Kotlin正式成为Android的一级开发语言。

Google在2019年的I/O大会上宣布Kotlin成为了Android第一开发语言,后续也将优先提供Kotlin版本的API使用说明。

Kotlin是由JetBrains公司开发与设计的,2016年发布了1.0正式版本。

特性

Kotlin和Java一样,都是在编译之后变成字节码文件,然后由虚拟机解释执行。其中字节码相当于某种中间语言,由更高级的抽象语言解析编译得来。

Kotlin可以直接调用Java编写的代码,也可以无缝使用Java的第三方开源库。

语法

kotlin

fun main(args: Array<String>) {
	println("Hello World!")
	println("Program arguments: ${args.joinToString()}")
}

Java函数入口点也必须在一个类中,Kotlin相比之下则不太相同,只需要定义main方法即可。

  • val:指的是value,用于声明一个不可变的变量,对应Java的final。一般优先使用val是个好习惯。
  • var:指的是variable,声明一个可变的变量,对应Java中非final变量

Kotlin默认使用类型推导机制 val a = 9  但是也可以显式赋值,类似下面这样 val a: Int = 9 Kotlin已经完全不使用基本数据类型了,全部使用对象数据类型。 这里的8大数据类型,都是原来Java的基本数据类型的关键词,并且第一个字母大写

函数的定义方法

kotlin

fun methodName(param1: Int, param2: Int): Int {
	return 0
}
  • if相比Java唯一的变化就是,它可以有返回值了。

可以赋值

kotlin

fun largerNumber(num1: Int, num2: Int): Int {
	val value = if (num1 > num2) {
		num1
	} else {
		num2
	}
	return value
}

也可以直接通过return返回

kotlin

fun largerNumber(num1: Int, num2: Int): Int {
	return if (num1 > num2) {
		num1
	} else {
		num2
	}
}

甚至可以通过下面的语法糖使用

kotlin

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

以及压缩成一行 fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) num1 else num2

  • when的逻辑控制 原始例子

kotlin

fun getScore(name: String) = if (name == "Tom") {
	86
} else if (name == "Jim") {
	77
} else if (name == "Jack") {
	95
} else if (name == "Lily") {
	100
} else {
	0
}

使用when进行编写

kotlin

fun getScore(name: String) = when (name) {
	"Tom" -> 86
	"Jim" -> 77
	"Jack" -> 95
	"Lily" -> 100
	else -> 0
}

when的完整格式是下面这样子 匹配值 -> { 执行逻辑 } 其中花括号可以省略

when甚至可以进行类型匹配

kotlin

fun checkNumber(num: Number) {
	when (num) {
		is Int -> println("number is Int")
		is Double -> println("number is Double")
		else -> println("number not support")
	}
}

其中is关键词相当于Java的instanceof

when也可以不传入参数使用

kotlin

fun getScore(name: String) = when {
	name.startsWith("Tom") -> 86
	name == "Jim" -> 77
	name == "Jack" -> 95
	name == "Lily" -> 100
	else -> 0
}

其中字符串或对象是否相等直接使用==判断即可,不需要Java那样使用equals方法

  • while循环的写法和Java完全一致
  • for-i循环被移除,但是for-each循环被大幅加强,变成了for-in循环。

关于区间的概念 val range = 0..10 上述代码表示创建了一个0到10的区间,并且两端都是闭区间,这意味着0到10这两个端点都是包含在区间中的,用数学的方式表达出来就是[0, 10]

val range = 0 until 10 使用until关键字来创建一个左闭右开的区间[0, 10)

有了区间之后,可以通过for-in循环来遍历这个区间

kotlin

fun main() {
	for (i in 0..10) {
		println(i)
	}
}

使用step图跳过元素

kotlin

fun main() {
	for (i in 0 until 10 step 2) {
		println(i)
	}
}

相当于for-i循环中i = i + 2的效果

使用downTo关键字创造降序区间

kotlin

fun main() {
	for (i in 10 downTo 1) {
		println(i)
	}
}

这里创建了一个[10, 1]的降序区间

一个类,类似下面这样的结构

kotlin

class Person {
	var name = ""
	var age = 0

	fun eat() {
		println(name + " is eating. He is " + age + " years old.")
	}

}

实例化的时候,并不需要像Java一样添加new关键词

kotlin

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

继承和构造函数

Kotlin中,只有抽象类可以被继承,在类名前加上open关键词,即可声明一个抽象类。

kotlin

open class Person {
	...
}

Kotlin中的继承类似于C/C++的风格,不是使用extends关键词,而是直接使用冒号。

kotlin

class Student : Person() {
	var sno = ""
	var grade = 0
}

关于主构造函数以及初始化结构体

每个类默认都有一个不带参数的主构造函数,主构造函数的特点是没有函数体,如果有需要在构造函数中执行的流程,可以写在init结构中。

kotlin

class Student(val sno: String, val grade: Int) : Person() {
	init {
		println("sno is " + sno)
		println("grade is " + grade)
	}
}

如果父类的主构造函数包含了需要传入的参数,则直接在继承时的括号中进行传入,例子如下:

kotlin

open class Person(val name: String, val age: Int) {
	...
}

class Student(val sno: String, val grade: Int, name: String, age: Int) :
	Person(name, age) {
	...
}

val student = Student("a123", 5, "Jack", 19)

当一个类既有主构造函数,又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用)。以下是一个例子:

kotlin

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

}

val student1 = Student()
val student2 = Student("Jack", 19)
val student3 = Student("a123", 5, "Jack", 19)

次构造函数通过constructor关键字来定义。

一个类中可以没有主构造函数,只使用次构造函数,此时类的声明和Java是类似的。

kotlin

class Student : Person {
	constructor(name: String, age: Int) : super(name, age) {
	
	}
}

在次构造函数中通过super调用父类的构造函数。

由于没有主构造函数,在继承Person类的时候也不需要写括号了

Kotlin的接口和Java几乎完全一致,一个类只能继承一个父类,但是可以实现任意数量的接口。

定义方式如下:

kotlin

interface Study {
	fun readBooks()
	fun doHomework()
}

方法只是声明,没有函数体。

kotlin

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.")
	}
}

在Kotlin中直接使用逗号分隔接口即可实现。

使用override关键字来重写父类或实现接口中的函数。

可以下面的方式调用接口中的方法:

kotlin

fun main() {
	val student = Student("Jack", 19)
	doStudy(student)
}

fun doStudy(study: Study) {
	study.readBooks()
	study.doHomework()
}

Kotlin支持在接口中写默认实现,如果实现该接口的类没有实现默认实现方法,也是没有问题的。

kotlin

interface Study {
	fun readBooks()
	fun doHomework() {
		println("do homework default implementation.")
	}
}
修饰符 Java Kotlin
public 所有类可见 所有类可见(默认修饰)
private 当前类可见 当前类可见
protected 当前类、子类、同一包路径下的类可见 当前类、子类可见
default 同一包路径下可见(默认修饰)
internal 同一模块中的类可见

需要使用修饰符的时候,直接加到fun关键词前面即可。

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

Kotlin直接在一个类前面添加data关键词即可声明一个数据类。

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

之后Kotlin会自动生成equals()、hashCode()、toString()等固定且无实际逻辑意义的方法。

并且后续判断可以直接使用==进行判断。

kotlin

fun main() {
    val cellphone1 = Cellphone("Samsung", 1299.99)
	val cellphone2 = Cellphone("Samsung", 1299.99)
	println(cellphone1)
	println("cellphone1 equals cellphone2 " + (cellphone1 == cellphone2))
}

Kotlin创建一个单例类,只需要将class关键词改为object即可。

kotlin

object Singleton {
	fun singletonTest() {
		println("singletonTest is called.")
	}
}

调用的时候直接进行类似Java中的静态调用。 Singleton.singletonTest() Kotlin保证全局只创建一个Singleton类实例。

集合创建与遍历 需要注意的是,下面的例子都可以使用Java的风格来写

  • 通过listOf()函数来简化初始化集合写法(创建的事不可变集合) val list = listOf(“Apple”, “Banana”, “Orange”, “Pear”, “Grape”) 并且可以使用for-in来遍历集合

kotlin

fun main() {
	val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
	for (fruit in list) {
		println(fruit)
	}
}
  • 如果需要创建一个可变集合,则需要使用mutableListOf()函数

kotlin

fun main() {
	val list = mutableListOf("Apple", "Banana", "Orange", "Pear", "Grape")
	list.add("Watermelon")
	for (fruit in list) {
		println(fruit)
	}
}
  • 对应的,set可以使用下面的Lambda方式编程,即setOf()与mutableSetOf()

  • Map的新增来如下用法

kotlin

val map = HashMap<String, Int>()
map["Apple"] = 1
map["Banana"] = 2
map["Orange"] = 3
map["Pear"] = 4
map["Grape"] = 5
val number = map["Apple"]
  • Map也是通过类似的lambda函数进行初始化,mapOf()和mutableMapOf()

val map = mapOf(“Apple” to 1, “Banana” to 2, “Orange” to 3, “Pear” to 4, “Grape” to 5)

这里的to不是关键字,而是infix函数。

下面是对map做遍历的方法,直接将Map的键值对变量一起声明到一个括号里面,遍历的时候会自动赋值给这两个键值对变量。

kotlin

fun main() {
	val map = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4, "Grape" to 5)
	for ((fruit, number) in map) {
		println("fruit is " + fruit + ", number is " + number)
	}
}

原文链接:https://ovea-y.cn/kotlin_lang_simple_learn/