Kotlin官方文档
https://kotlinlang.org/docs/home.html
中文网站
https://www.kotlincn.net/docs/reference/properties.html
本系列为参考Kotlin中文文档
https://download.csdn.net/download/u011109881/21418027
整理的笔记
pdf也可以在这里下载
https://www.kotlincn.net/docs/kotlin-docs.pdf
大部分示例来自bilibili Kotlin语言深入解析 张龙老师的视频
知识点
1 函数的默认参数
2 函数默认值在函数覆盖时的注意点
3 无默认值参数在有默认值参数
4 函数最后一个参数是一个函数
5 可变参数vararg
6 * spread operator 分散运算符
7 具名参数
8 函数简写
9 单表达式函数
10 显式返回类型
11 可变参数的位置
笔记
// 函数
fun main() {
// 1 函数的默认参数
test()
test(2)
// b 是具名参数
// 显示指定参数名
test(b = 3)
test(3, 2)
test(a = 3)
// 2 函数默认值在函数覆盖时的注意点
// 3 无默认值参数在有默认值参数
// 这种情况如果想省略参数 只能通过具名参数的方式来使用
test2(1, 2) // 要么不省略任何参数
test2(b = 3) // 要么 只能使用具名参数
// 4 函数最后一个参数是一个函数
// 调用已有方法
test3(2, 3, ::test)
// 另外新写方法
test3(4, 5, { a, b -> println(a - b) })
// 将上面的方法缩写
// 即 函数最后一个参数是一个lambda表达式 可以将其移动到小括弧外部
test3(4, 5) { a, b -> println(a - b) }
// 省略一个参数
test3(4) { a, b -> println(a - b) }
test3(b = 4) { a, b -> println(a - b) }
// 调用函数时 如果同时使用了位置参数和具名参数,那么所有位置参数必须要位于第一个具名参数之前
// 例如 foo(1,x=2) OK foo(x=2,2) NG
// 5 可变参数vararg
test4("a", "b", "c")
test4New("a", "b", "c")
// 6 * spread operator 分散运算符
val arrays:Array = arrayOf("str1", "str2", "str3")
// 不能直接传入string数组 Kotlin会以为只有一个参数 Type mismatch. Required: String Found: Array
// test4(strings) // 报错
// 需要使用 * 字符拆分符 将string数组拆分为多个参数
test4(/*strings =*/*arrays)
test4(/*strings =*/*arrayOf("a","b"))
test4("a")
// 7 具名参数
// 在Kotlin中调用Java代码时不能使用具名参数 因为Java字节码不总是会保留参数名信息
// 即 具名参数不能用在Kotlin调用Java代码时
// 8 函数简写
myPrintNew("======")
// 9 单表达式函数
// 当函数返回单个表达式时,可以省略花括号并且在 = 符号之后指定代码体即可
add(1,2)
// 10 显式返回类型
// 拥有方法体或者说块体的方法(即花括号)需要定义显式的返回类型 除非该方法返回Unit
// Kotlin 不会推断拥有方法体或者说块体的方法的返回值类型 因为这种函数可能有非常复杂的控制流程
// 对于阅读代码的人和编译器来说 返回类型不是那么明显了 因此Kotlin统一不对所有显示返回类型的方法做类型推断
// 例如addNew就是一个 显示返回类型的方法
// 11 可变参数的位置
// 一个方法中 只允许有一个参数为可变参数,通常是最后一个参数 但是有例外
// 如果vararg不是最后一个参数 那么可变参数后面的参数需要通过具名参数进行传递
// 如果其后的参数是函数类型 可以通过圆括号外部传递lambda表达式来实现
println(convert2List("hello", "world"))
val elements = arrayOf("welcome", "kotlin", "test")
println(convert2List("1", "2", elements))
println(convert2List("1", "2", *elements))//注意和上面区别
println(convert2ListNew("hello", "world", a = "1", b = "2"))
println(convert2ListNew2("hello", "world") { a, b ->
println("a is $a b is $b")
})
println(convert2ListNew2("hello") { a, b ->
println("a is $a b is $b")
})
}
// 11 可变参数的补充
fun convert2List(vararg element: T): List {
val res = ArrayList()
element.forEach { res.add(it) }
return res
}
// 11 可变参数的补充 可变参数后面还有其他参数
fun convert2ListNew(vararg element: T, a: T, b: T): List {
val res = ArrayList()
element.forEach { res.add(it) }
res.add(a)
res.add(b)
return res
}
// 11 可变参数的补充 可变参数后面的参数是一个函数
fun convert2ListNew2(vararg element: T, myPrint: (x: T, y: T) -> Unit): List {
val res = ArrayList()
element.forEach { res.add(it) }
if (res.size >= 2) {
myPrint(res[0], res[1])
} else {
println("list too short")
}
return res
}
// 9 单表达式函数
// 函数的返回类型如果可以通过类型推断判断出来 那么返回类型可以省略
fun add(a: Int, b: Int) = a + b
fun addNew(a: Int, b: Int): Int {
return a + b
}
// 8 函数简写
// 当返回值为Unit时可以简写
fun myPrint(name: String) {
println(name)
}
// 完整写法
fun myPrintNew(name: String): Unit {
println(name)
return Unit
}
// 5 可变参数vararg
fun test4(vararg strings: String) {// strings 属于string数组类型 // 注意string数组和Array 不一样
println(strings.javaClass)
strings.forEach { println(it) }
}
fun test4New(vararg strings: String) {
println(strings.javaClass)
strings.forEach { a ->
println(a)// 当只有一个参数时 可以简写为上面那种形式 println(it)
}
}
// 4 函数最后一个参数是一个函数
// 如下 第三个参数是一个接受2个参数 返回值为空的函数
fun test3(a: Int = 1, b: Int = 2, compute: (x: Int, y: Int) -> Unit) {
compute(a, b)
}
// 3 无默认值参数在有默认值参数
fun test2(a: Int = 0, b: Int) = println(a + b)
// 2 函数默认值在函数覆盖时的注意点 start
open class A {
open fun function(a: Int, b: Int = 4) = a + b
}
class B : A() {
// 对于重写的方法来说 子类重写的方法会使用父类方法的参数默认值 即使父类方法的参数没有默认值
// 子类也不能拥有自己参数的默认值
// 个人理解 这里Kotlin的设计是为了避免函数默认值混乱
// An overriding function is not allowed to specify default values for its parameters
override fun function(a: Int, b: Int) = a + b
}
// 2 函数默认值在函数覆盖时的注意点 end
// 1 函数的默认参数
fun test(a: Int = 0, b: Int = 1) {
println(a - b)
}
// 如果函数体只有一句话 可以去掉{} 写成如下形式
fun testNew(a: Int = 0, b: Int = 1) = println(a - b)
class D0401Functions {
}
知识点
1 中缀符号 (infix notation)
2 内联函数
3 高阶函数(high-order function)和 Lambda 表达式
笔记
fun main() {
// 1 中缀符号 (infix notation)
// 具体作用主要是 忽略该调用的点与圆括号
// 函数定义成中缀函数有3个条件
// a 是成员函数或扩展函数 (依赖于某一个类)
// b 只有一个参数 且没有默认值(不可以是可变参数)
// c 使用infix修饰
val infixTest = InfixTest(2)
println(infixTest.add(5))
println(infixTest add 5) //中缀表达式的写法
/**
* 2 内联函数
* 2.1 内联函数的目的
* 以下为个人理解
* 函数之间的调用会有函数堆栈 进入下一个函数前 当前函数的信息会以存入堆栈 知道从下一个函数返回才会取出
* 因此 函数调用层级越高 会导致调用堆栈异常庞大 在递归函数中如果发生无法终止递归的情况 会一直运行直到堆栈溢出
* 内联函数的目的是为了避免函数堆栈过大设计出来的
* 调用到内联函数时 会将该函数复制到调用处 这样 在同一个函数里 就不会创建创建函数调用堆栈存放数据了
* 2.2 内联函数的缺点
* 以下为个人理解
* 虽然内联函数一定程度上降低了调用堆栈的高度 但是 如果内联函数被调用的次数太多 那么就需要不断的拷贝
* 这种操作消耗的资源也不见得很小 所以 内联函数的作用其实有限
* 2.3 内联函数的示例
* 通过反编译kotlin为Java 我们可以轻易看出区别
* 或者通过 javap xx.D0401Functions2 javap -c xx.D0401Functions2 反编译出字节码 也可以看出区别
*/
println(myInlineFun(1, 2))
println(myNormalFun(1, 2))
println("=====2 end======")
/**
* 3 高阶函数(high-order function)和 Lambda 表达式
* 一个函数 如果它接受一个函数作为参数或者返回一个函数 那么这就是高阶函数
* lambda 表达式的要求
* a lambda表达式被花括号包围
* b 参数位于 —> 之前 (参数类型可以省略)
* c 执行体位于 -> 之后
* 在kotlin中 如果参数的最后一个参数是一个函数 那么可以将lambda表达式作为实参传递 并且 放在圆括号之外的一对花括号中
*/
println(oneParameter(2))
println("===== 3 end======")
}
// lambda 表达式示例
val multiply: (Int, Int) -> Int = { a: Int, b: Int -> a * b } // 最完整写法
val multiply2: (Int, Int) -> Int = { a, b -> a * b } // 简写1
val multiply3 = { a: Int, b: Int -> a * b } // 简写2
val myAction = { println("hello world") } // 无参的lambda表达式
val myAction2: () -> Unit = { println("hello world") } // 完整写法
val myReturnNull: (Int, Int) -> Int? = { a, b ->
if (a + b > 0) {
a + b
} else {
null
}
}
// 下划线 变量占位符 如果变量在函数中没有使用 可以使用变量占位符代替
val myReturnNull2: (Int, Int) -> Int? = { _, _ -> null }
// 当只有一个参数时 可以用it代替
val oneParameter: (Int) -> Int = { it -> it + 1 }
// 整个函数可能为空
val functionMayNull: ((Int, Int) -> Int)? = null
// 2 内联函数对比普通函数
inline fun myInlineFun(a: Int, b: Int): Int = a + b
fun myNormalFun(a: Int, b: Int): Int = a + b
// 1 中缀函数demo
class InfixTest(val a: Int) {
infix fun add(b: Int) = this.a + b
}
class D0401Functions2
知识点
1 高阶函数
2 高阶函数的应用
3 高阶函数的应用2
4 高阶函数的应用3
5 lambda表达式的返回值
6 匿名函数
7 闭包
8 带有接收者的函数字面值
9 函数字面值结合匿名函数
10 函数字面值扩展
笔记
fun main() {
/**
* 1 高阶函数
*/
myCalculate(1, 3, { a, b -> a + b })
myCalculate(1, 3) { a, b -> a + b }
myCalculate(1, 3, ::myAdd)
println("====== 1 end =======")
// 2 高阶函数的应用
// 过滤掉字符串中所有非字母字符
println("123hellow123".filter { it.isLetter() })
println("123ffff".filter { a -> a.isLetter() })
println("he1llo123".filter2 { a -> a.isLetter() })
println("====== 2 end =======")
// 3 高阶函数的应用2
// 筛选字符串数组中包含字符H h的字符串
val strings = arrayOf("hello", "world", "kotlin", "hi")
// 复杂写法
filterStrings(strings) { string -> string.contains("H") || string.contains("h") }
// 简化写法
strings.filter { it.contains("h", ignoreCase = true) }.forEach { println(it) }
println("-----")
// 筛选字符串数组中字符串长度=5的
// 复杂
strings.filter { string -> string.length == 5 }.forEach { string -> println(string) }
// 简化
strings.filter { it.length == 5 }.forEach { println(it) }
println("====== 3 end =======")
// 4 高阶函数的应用3
// 找出字符串数组中包含D或d的字符串 将其改为大写输出
strings.filter { it.contains("d", true) }.forEach { println(it.uppercase(Locale.getDefault())) }
println("====== 4 end =======")
/**
* 5 lambda表达式的返回值
* 在默认情况下 lambda表达式的最后一句话会作为该lambda表达式的返回值
* 我们可以全限定的return语法显示从lambda表达式返回值
*/
strings.filter {
val needFilter = it.length == 5
needFilter //lambda表达式的默认返回值
}
// 什么是 全限定的return语法
strings.filter {
val needFilter = it.length == 5
return@filter needFilter // 全限定的return语法 会使用到标签 用法为return@方法名 用来表示lambda的返回
// 此种用法可以使用 但是通常不用 可读性不高
}
println("====== 5 end =======")
/**
* 6 匿名函数
* 匿名函数与普通函数的区别
* 匿名函数的参数类型如果可以推断出来 可以省略
* 匿名函数不能之间定义在顶层(因为那样就无法调用了)
* 匿名函数所有参数必须在圆括号中 参数是lambda表达式也一样
* 匿名函数主要可以代替lambda表达式 (实际意义不大)
* 匿名函数的return是返回匿名函数本身 而lambda表达式则不同 之间return表示返回到外层函数
* 匿名函数的使用开起来不如lambda 了解即可
*/
// 定义了两个无法使用的匿名函数
fun(x: Int, y: Int) = x + y
fun(x: Int, y: Int): Int {
return x + y
}
val strings2 = arrayOf("hello", "world", "kotlin", "hi")
// lambda
strings2.filter { it.length == 5 }.forEach { println(it) }
// 匿名函数代替lambda
strings2.filter(fun(item: String): Boolean { return item.length == 5 }).forEach(fun(item) { println(item) })
println("====== 6 end =======")
/**
* 7 闭包
* Lambda 表达式或者匿名函数(以及局部函数和对象表达式) 可以访问其闭包 ,
* 即在外部作用域中声明的变量。 在 lambda 表达式中可以修改闭包中捕获的变量
* 如下 forEach中可以访问闭包中的 sum
*/
var sum = ""
val strings3 = arrayOf("hello", "world", "kotlin", "hi")
strings3.filter { it.contains("h") }.forEach { sum += it }
println(sum)
println("====== 7 end =======")
/**
* 8 带有接收者的函数字面值
* 这个功能及其类似扩展函数
* 带有接收者的函数类型,例如 A.(B) -> C,可以用特殊形式的函数字面值实例化—— 带有接收者的函数字面值。
* 如上所述,Kotlin 提供了调用带有接收者(提供接收者对象)的函数类型实例的能力。
* 在这样的函数字面值内部,传给调用的接收者对象成为隐式的this,以便访问接收者对象的成员而无需任何额外的限定符,亦可使用this表达式访问接收者对象。
* 这种行为与扩展函数类似,扩展函数也允许在函数体内部访问接收者对象的成员。
*
* 下面的变量是函数类型 Int的类似于扩展函数 接受一个int参数 返回一个int类型 {}里为方法体
* 个人理解:
* 接收者类型为Int 它在点号前面
* 点号后面是函数字面值
* 在函数内部可以使用this代表函数调用者
*/
val subtract: Int.(other: Int) -> Int = { other -> this - other }
// 等价于如下
val subtract2: Int.(other: Int) -> Int = { this - it }
println(3.subtract(4))
println("====== 8 end =======")
/**
* 9 函数字面值结合匿名函数
* 匿名函数语法允许你直接指定函数字面值的接收者类型。 如果你需要使用带接收者的函数类型声明一个变量,并在之后使用它,这将非常有用 -- 来自官网
*
*/
// 使用匿名函数的case 单表达式
val sum2: Int.(Int) -> Int = fun Int.(other: Int): Int = this + other
// 使用匿名函数的case 变体
val sum3: Int.(Int) -> Int = fun Int.(other: Int): Int { return this + other }
// 使用lambda表达式的case
// 对比lambda的形式 完全不知道 “这将非常有用” 有用在哪?
val sum4: Int.(Int) -> Int = { this + it }
println(1.sum2(2))
println(1.sum4(2))
println("====== 9 end =======")
/**
* 10 函数字面值扩展
* 带有接收者类型的函数的非字面值 可以作为参数传递 前提是所需要接受函数的地方应该有一个接收者类型的参数
* 类型 String.(Int) -> Boolean 等价于 (String,Int) -> Boolean
*/
// myEquals是一个函数 并且使用了带有接收者的函数字面值 是String的一个方法 方法体
// toIntOrNull是string的一个方法 当前如果可以转为int则返回其int值否则返回null
val myEquals: String.(Int) -> Boolean = { it -> this.toIntOrNull() == it }
println("456".myEquals(456))
println("156".myEquals(456))
// myTestEquals 是一个方法 接受3个参数 第一个参数是个方法,接受string和int参数 返回Boolean值
fun myTestEquals(op: (String, Int) -> Boolean, a: String, b: Int) = println(op(a, b))
myTestEquals(myEquals, "456", 456)
myTestEquals(myEquals, "156", 456)
// 可以看到myTestEquals第一个参数明明是(String, Int) -> Boolean 却可以将myEquals传入myTestEquals
// 可见String.(Int) -> Boolean 等价于 (String,Int) -> Boolean
println("====== 10 end =======")
}
// 3 高阶函数的应用2
fun filterStrings(strings: Array, filter: (String) -> Boolean) {
for (string in strings) {
if (filter(string)) {
println(string)
}
}
}
// 2 高阶函数的应用 start
fun String.filter(predicate: (Char) -> Boolean): String {
val buffer = StringBuffer()
for ((index, chars) in this.withIndex()) {
if (predicate(chars)) {
buffer.append(chars)
}
}
return buffer.toString()
}
fun String.filter2(predicate: (Char) -> Boolean): String {
val buffer = StringBuffer()
for (index in this.indices) {
if (predicate(this[index])) {
println("[$index] is letter")
buffer.append(this[index])
} else {
println("[$index] not letter")
}
}
return buffer.toString()
}
// 2 高阶函数的应用 end
/**
* 1 高阶函数示例
* 该函数接受三个参数 第三个参数为一个名为calculate的函数
* calculate函数实际只是个形参 规定了外部传入的函数接受2个int返回一个Int
* 具体算法由外部决定
*/
fun myCalculate(a: Int, b: Int, calculate: (Int, Int) -> Int): Int {
return calculate(a, b)
}
fun myAdd(a: Int, b: Int): Int {
return a + b
}
class D0401Functions3