1.可变参数
可变参数使用vararg关键字定义。
如果可变参数是基本数据类型,那么接受的是基本类型数组,而不是包装类数组。比如整型的可变参数,接受的的是IntArray而不是Array,IntArray底层是int[],而Array底层是Integer[],所以List集合用于可变参数时需要调用toTypedArray()后,再调用toIntArray(),才能转为IntArray。
spread operator涉及到数组复制,性能消耗较高,所以要尽量减少spread operator的使用,可以在函数中使用list
fun intVarargFun(vararg numbers:Int){
println(numbers)
}
fun testIntVarargFun(){
val numbers = listOf(1,2).toTypedArray().toIntArray()
intVarargFun(*numbers)
}
2.中缀函数
只有一个参数的成员函数或者扩展函数,在调用是可以省略点和小括号,形式上更像数学表达式。Kotlin有很多自带的中缀函数,比如to,intersect,union等。
如果中缀函数的接受者对象类型,参数类型,以及返回值类型相同时,中缀函数可以连续使用
fun testInfixFun(){
val result = listOf(1,2) intersect listOf(2,3)//输出为[2],result类型为LinkedHashSet,如果要返回ArrayList需要result调用toList
val result2 = listOf(1,2) union listOf(2,3) union listOf(3,4)//输出为[1,2,3,4]
}
自定义中缀函数使用infix关键字,比如将定义List的union中缀函数
infix fun <T> List<T>.union(other List<T>):List<T> {
return this.toMutableSet().apply{
addAll(other)
}.toList()
3.运算符重载
在指定范围内,重载特定的函数,执行我们定义的逻辑。比如定义重载plus函数,编译器会将plus与+关联起来,直接使用+即可
private class UnionArrayList<T>(collection:Collection<T>):ArrayList<T>(collection){
operator fun plus(other:UnionArrayList<T>):UnionArrayList<T>{
val list = this.toMutableSet().apply{addAll(other)}
return UnionArrayList(list)
}
}
private fun <T> unionListOf(vararg element:T):UnionArrayList<T>{
return UnionArrayList(listOf(*elements)
}
fun testOperatorPlus(){
val result = unionListOf(1,2)+ unionListOf(2,3)
println(result)
}
4. 局部函数
在函数(外部函数)中定义的函数称为局部函数,局部函数可以访问外部函数体中的变量,局部函数与外部函数是组合关系。比如二分查找的入口函数(外部函数)和递归函数(局部函数),递归函数作为入口函数的一部分 ,可以直接访问入口函数的变量,不必再通过参数
fun main(args: Array<String>) {
val result = binarySearch(arrayOf(1, 2, 3, 4, 5).toIntArray(), 3)
println(result)
val result2 = binarySearch(arrayOf(1, 2, 3, 4, 5).toIntArray(), 1)
println(result2)
val result3= binarySearch(arrayOf(1, 2, 3, 4, 5).toIntArray(), 5)
println(result3)
}
private fun binarySearch(numbers: IntArray, target: Int): Int {
fun binarySearchRecursive(start: Int, end: Int): Int {
if(start>end) return -1
val middle :Int = (start+end)/2
val middleValue:Int = numbers[middle]
return when {
middleValue==target -> middle
middleValue<target -> binarySearchRecursive(middle+1,end)
else -> binarySearchRecursive(start,middle-1)
}
}
return binarySearchRecursive(0, numbers.size - 1)
}
5.尾递归函数
如果一个递归函数只在自己函数体的最后才调用自己,则属于尾递归函数。这时在函数前面前加tailrec关键字,可以让kotlin编译器将递归转换成循环。
循环相对于递归不会有StackOverflow栈溢出的风险。虚拟机内存远远大于最大堆栈深度,如果处理一个规模比较大的数据,使用循环内存不大会溢出,但是使用递归就很容易使堆栈深度超过虚拟机最大堆栈深度而导致奔溃。比如反转字符串,如果字符串很长的话,用递归的方法交换字符串两边的字符,递归深度等于字符长度的一半,很容导致堆栈溢出。而使用循环只是需要一块内存把字符串放进去就够了,对内存没有很大压力。
private fun binarySearch(numbers: IntArray, target: Int): Int {
//使用tailrec关键字
tailrec fun binarySearchRecursive(start: Int, end: Int): Int {
if(start>end) return -1
val middle :Int = (start+end)/2
val middleValue:Int = numbers[middle]
return when {
middleValue==target -> middle
middleValue<target -> binarySearchRecursive(middle+1,end)
else -> binarySearchRecursive(start,middle-1)
}
}
return binarySearchRecursive(0, numbers.size - 1)
}
6.object表达式,对象声明,内部类,Lambda表达式,匿名函数
6.1 内部类
1)方法中的内部类,可以访问所在方法体中的变量;内部类可以继承另外一个类;不能用private,inner修饰;内部类调用必须放在定义之后。
java中内部类不能
2)类中的内部类,必须使用inner关键字定义;内部类包含着对外部类实例的引用,在内部类可以访问外部类中的属性;可以定义多个内部类,内部类继承与外部类无关的类,实现多继承。可以使用private修饰内部类,使得其他类不能访问,具有良好的封闭性。
open class Horse{
fun runFast(){
println("I can run fast")
}
}
open class Donkey{
fun doLongTimeThing(){
println("I can do some thing long time")
}
}
private inner class HorseC:Horse()
private inner class DonkeyC:Donkey()
fun runFast(){
HorseC().runFast()
}
fun dongLongTimeThing(){
DonkeyC().doLongTimeThing()
}
object表达式和匿名内部类很相似。
差异点:object表达式可以赋值给一个变量