一、基本概念
1. 函数
1.1 基本形式
fun showMessage(message: String?): Unit {
message?.let {
println(it)
}
}
1.2 参数使用默认值
fun showName(name: String, age: Int = 2) : Unit {
println("My name is $name, my age is $age years old")
}
1.3 函数返回值
fun doubleOf(number: Int): Int {
return number + number;
}
1.4 函数简单形式
fun total(single: Double, count: Int, percent: Double) : Double = single * count * percent
1.5 中缀函数
类的成员函数, 并且参数个数为1, 调用形式比较特殊, 函数的名字需要像操作符一样放在类对象和参数的中间用空格分开: obj op arg
常用中缀函数 to, “age” to 18 结果为 Pair(“age”, 18)
infix fun Char.repeat(times: Int) = "${this}".repeat(times)
1.6 操作符函数
/// 比如定义乘法操作符 *
/// 整数 * 字符串 => 重复(整数次)输入字符串的字符串
operator fun Int.times(message: String) = message.repeat(this)
1.7 vararg参数
/// 表示数量不定的同类型参数
/// 在运行期间, vararg 标记的参数会表现为数组, 如果需要在函数内部传递给另一个vararg 参数的函数, 需要用 * 来引用
fun multiPrint(vararg messages: String) {
for (i in messages) {
print(i)
print(if (i === messages.last()) "" else ", ")
}
}
2. 常量和变量
2.1 常量的定义,
- 使用 val 关键字, 形式: val 常量名[: 常量类型] = 常量值
/// 定义时必须初始化, 可以通过类型推断省略常量的类型标志
/// 常量定义后, 不可再修改常量的值, 否则编译报错
val n: Int = 100
val name = "God"
2.2 变量的定义
- 使用 var 关键字, 形式: var 变量名[: 变量类型] = 初始值
/// 变量同样在定义时必须给定初始值, 及时是可空/可选类型, 也要使用null进行初始化
var number: String? = null
var age: Int = 0
var message = "Hahahaha"
2.3. 空值安全保障
- kotlin中规定:不可以对任何类型的变量赋值为null, 如果认定为该变量可以为null, 则必须将该变量定义为可空类型, 即在原来的类型后面加上?
- 并且可空类型的变量也不可传递到非可空类型参数的函数中
- 如果函数的参数类型有可能为空, 则应该使用可空类型的参数
var myName = "oyoung"
/// myName = null // 不允许
var yourName: String? = "Big SB"
yourName = null
3. 类
3.1 不包含任何属性和自定义构造函数的类
class One
3.2 包含属性和自定义构造的类
class Two(val value: Int, var name: String)
fun classUsage() {
val one = One()
var two = Two(0, "O")
/// 3.3 访问类属性
println(two.value)
/// 3.4 修改类属性
two.name = "Fuck"
}
4 泛型
4.1 泛型类
class Queue<E>(vararg items: E) {
private val children = items.toMutableList()
fun enqueue(item: E) = children.add(item)
fun dequeue(): E = children.removeAt(0)
fun size() = children.size
fun isEmpty() = children.isEmpty()
}
4.2 泛型函数
fun <E> queueOf(vararg items: E) : Queue<E> = Queue(*items)
fun queueUsage() {
val qa = Queue(1, 2, 3, 4)
val qb = queueOf("a", "b", "c")
qa.enqueue(8)
qb.dequeue()
}
5. 继承
5.1 open关键字
- 在Kotlin里面, 所有的类默认都是final的, 即不可继承的,
- 如果需要将某各类设计成父类,需要加上open 关键字
- 如果需要重载父类的成员函数, 父类的函数也要加上open关键字
open class Animal {
open fun say() {
println("Nothing")
}
}
open class Dog(val name: String, var age: Int): Animal() {
override fun say() {
println("Wow Wow, My name is $name, I'm $age years old")
}
}
5.2 继承父类时,可以直接调用父类构造函数
class Keji: Dog("Keji", 0)
二、流程控制
1. when 语句, 加强版的 switch case
fun useWhen() {
for (i in 0..9) println( when(i) {
2 -> "i is 2"
is Int -> "i is int"
else -> "i is not 2"
})
}
2 循环
fun useLoops() {
println("for 循环")
/// 2.2.1 for 循环
for (i in 0..9) {
print(i)
print(if (i == 9) "" else ", ")
}
println()
println("while 循环")
/// 2.2.2 while 循环
var i = 0
while (i < 10) {
print(i++)
print(if (i == 10) "" else ", ")
}
println()
println("do {} while 循环")
/// 2.2.3 do {} while
do {
print(i--)
print(if (i == 0) "" else ", ")
} while (i > 0)
println()
println("迭代器循环")
/// 2.2.4 迭代器
/// 当集合类型重载了 iterator() 接口并返回一个 iterator时,该集合类型的对象可以直接使用 for ( in ) 循环
/// iterator的特征是 拥有以下两个接口函数
/// hasNext() -> Boolean
/// next() -> E
val list = listOf(0, 1, 2, 3, 4)
val it = list.iterator()
while (it.hasNext()) {
print(it.next())
print(if (it.hasNext() ) ", " else "")
}
println()
}
3. 范围
fun useRange() {
/// 2.3.1 使用 ..
val range1 = 0..5 // 0, 1, 2, 3, 4, 5
/// 2.3.2 使用 .. + step
val range2 = 0..6 step 2 // 0, 2, 4, 6
/// 2.3.3 使用 downTo
val range3 = 5 downTo 0 // 5, 4, 3, 2, 1, 0
/// 2.3.4 使用 downTo + step
val range4 = 50 downTo 0 step 10 // 50, 40, 30, 20, 10, 0
println("Range: ")
range1.forEach {
print(it)
print(if (it == range1.last) "" else ", ")
}
println()
range2.forEach {
print(it)
print(if (it == range2.last) "" else ", ")
}
println()
range3.forEach {
print(it)
print(if (it == range3.last) "" else ", ")
}
println()
range4.forEach {
print(it)
print(if (it == range4.last) "" else ", ")
}
println()
}
4. 相等比较
fun useEquals() {
val a = setOf(1, 2, 3)
val b = setOf(3, 2, 1)
/// 2.4.1 == 值比较
println(a == b)
/// 2.4.2 === 引用比较
println(a === b)
}
5. 条件表达式 if else
- kotlin 的条件表达式可以有返回值
- 其他语言中的三目运算符 a ? b: c 在kotlin 中仍旧是 if (a) b else c
fun useConditionExpression() {
var number = 9
if (number % 2 == 0) {
println("偶数")
} else {
println("奇数")
}
println( if(number % 3 == 0) "是3的倍数" else "不是3的倍数" )
}
三、特殊类
1. 数据类(data class)
- 数据类表示用来存储一组相关数据,
- 定义数据类时, kotlin会自动为定义的类生成copy方法和toString方法
data class User(var id: Int, var name: String)
fun useDataClass() {
val xiaoming = User(0, "xiaoming")
/// 自动生成的 toString 方法
println(xiaoming)
/// 自动生成的copy 方法
val xiaoming2 = xiaoming.copy()
println(xiaoming2)
/// 自动生成的 copy 方法有和构造函数一样的参数列表
val xiaohong = xiaoming.copy(1, "xiaohong")
println(xiaohong)
/// 自动生成的相等判断会认为 所有属性值都相等时两个对象相等
/// 相等的 data class 对象有相同的 hashCode
println("xiaoming hashCode: ${xiaoming.hashCode()}, xiaoming2 hashCode: ${xiaoming2.hashCode()}, xiaoming == xiaoming2 result: ${xiaoming == xiaoming2}")
println("xiaohong hashCode: ${xiaohong.hashCode()}, xiaoming == xiaohong result: ${xiaoming == xiaohong}")
/// 自动生成 componentN() 方法 用来获取按定义时的顺序对应的属性
println(xiaoming.component1() == xiaoming.id)
println(xiaoming.component2() == xiaoming.name)
}
2. 枚举类
枚举类使用关键字 enum class 定义
enum class Fruit {
Apple, Pear, Banana
}
枚举类定义时可以带有属性, 还可以带有方法
/// 在定义方法前, 需要使用在最后一个枚举值后面加上分号(;)
enum class Color(val r: Int, val g: Int, val b: Int) {
RED(255, 0, 0),
GREEN(0, 255, 0),
BLUE(0, 0, 255),
PURPLE(255, 0, 255),
YELLOW(255, 255, 0),
BLACK(0, 0, 0),
WHITE(255, 255, 255);
fun containsRed() = this.r != 0
fun containsBlue() = this.b != 0
}
fun useEnumClass() {
/// 获取枚举值
val fruit = Fruit.Banana
/// 当枚举类型使用when 语句时, 应该枚举出所有的值, 并且无需else 分支
println( when(fruit) {
Fruit.Apple -> "It's an apple"
Fruit.Pear -> "It's a pear"
Fruit.Banana -> "It's a banana"
})
val red = Color.RED;
println(red)
println(red.containsRed())
println(Color.YELLOW.containsBlue())
}
3. 密封类
- 密封类使用 sealed class 定义
- 使用密封类时, 只能在定义密封类的同一个文件中定义其子类, 不允许文件外定义任何子类
sealed class Dog(val type: String, var name: String)
class Hashiqi(var hashiqiName: String): Dog("hashiqi", hashiqiName)
class Keji(var kejiName: String): Dog("keji", kejiName)
fun kissDog(dog: Dog): String {
return when (dog) {
is Hashiqi -> "kiss a Hashiqi named ${dog.hashiqiName}"
is Keji -> "kiss a Keji named ${dog.kejiName}"
}
}
fun useSealedClass() {
val dawang = Keji("Dawang")
val xiaowang = Hashiqi("Xiaowang")
println(dawang)
println(xiaowang)
println(kissDog(dawang))
println(kissDog(xiaowang))
}
3.4 object 关键字
- 使用 object 关键字可以随时定义一个 带有定义属性和属性值的匿名类的对象
- 使用 class 定义的类, 在访问其属性时,需要先构造出该类的一个对象
- 使用 object 定义的对象, 可以直接访问其属性
fun useObject( ) {
val result = object {
val code = 404
val message = "Not Found"
}
println(result.code)
println(result.message)
}
范围函数
/// 四、范围函数
fun main(args: Array<String>) {
/// 4.1 let
/// let 用来做空值检测, 使用可选值对象时非常有用
var number: Int? = if(Math.random() < 0.5) 100 else null
/// 当调用 let 方法的对象非空时, let 方法返回参数block 内最后一行表达式的值
/// 否则直接返回 null
/// 在 block 中使用 it 来引用调用对象
val str = number?.let {
it.toString()
}
println(str)
/// 与let 相同功能的还有 run, 但是 run 方法有点不太一样, 在 block 使用 this 引用引用调用对象
/// 这样有个好处是可以直接调用 对象的所有成员方法而不需要任何对象名前缀
println(number?.run {
"run: ${toString()}"
})
/// with 不是像 let run 一样的扩展方法, 但是也可以像run 一样针对对象来使用, 同样在 block 中可以使用 this 引用来引用 with 的参数对象
with (number) {
println("with: ${toString()}")
}
/// 还有两个比较好用的 扩展方法 apply 和 also
/// 当调用对象非空时, 才会调用参数 block
/// 这两个方法 都是返回调用对象本身, 不同之处是 apply 的参数block 中, 使用this 引用调用对象, 而 also 的参数block 中, 使用 it 引用调用对象
number?.apply {
println("apply: ${toDouble()}")
}
number?.also {
println("also: $it")
}
}
扩展 defer
class Defer {
fun defer(block: () -> Unit) = blocks.add(block)
fun invokeAll() {
while (blocks.isNotEmpty()) {
blocks.removeAt(blocks.lastIndex)()
}
}
private val blocks = mutableListOf<() -> Unit>()
}
fun useDefer(block: Defer.() -> Unit) = Defer().apply(block).invokeAll()
fun main(args: Array<String>) {
useDefer {
println(1)
defer {
println(2)
}
println(3)
defer {
println(4)
}
}
}