kotlin学习笔记(5)--泛型和高级函数的应用

1.泛型函数

泛型的引入:函数的输入参数类型必须在定义函数时就要指定,可是有时候参数类型是不确定的,只有在函数调用时方能知晓具体类型,如此一来要怎样声明函数呢?
定义泛型函数时,得在函数名称前面添加“”,表示以T声明的参数(包括输入参数和输出参数),其参数类型必须在函数调用时指定。
实例:
fun appendString(tag:String, var otherInfo:T?):String{
           var str:String = "$tag:"
            for (item in otherInfo){
                   str="$str${item.toString()}"
            }
            return str
}
fun main(args: Array){
           var count = 0
           when(count%3){
                   0 -> appendString("古代的四大发明","造纸术","印刷术","火药","指南针")
                   1 -> appendString("小于10的素数",2,3,5,7)
                   else -> appendString("烧钱的日子",5.20,6.18,11.11,12.12)
           }
}

2.内联函数

注意到前面定义泛型函数appendString,是把它作为一个全局函数,也就是在类外面定义,不在类内部定义。因为类的成员函数依赖于类,只有泛型类(又称模板类)才能拥有成员泛型函数,普通类是不允许定义泛型函数的,否则编译器会直接报错。不过有个例外情况,如果参数类型都是继承自某种类型,那么允许在定义函数时指定从这个基类泛化开,凡是继承自该基类的子类,都可以作为输入参数进行函数调用,反之则无法调用函数。
举个例子,Int、Float和Double都继承自Number,但是定义一个setArrayNumber(array:Array)函数,它并不接受Array或者Array的入参,如果要让该方法同时接受源自Number的数组入参,就得定义泛化自Number的泛型函数,即将改为,同时在fun前面添加关键字inline,表示该函数也为内联函数。内联函数在编译之时,会在调用处把该函数的内部代码直接复制一份,调用十次就会复制十份,而非普通函数那样仅仅提供一个函数的访问地址。该例子的函数定义代码如下所示:
//该函数不接受Array,也不接受Array,只好沦为孤家寡人
    fun setArrayNumber(array:Array) {
        var str:String = "数组元素依次排列:"
        for (item in array) {
            str = str + item.toString() + ", "
        }
        tv_function_result.text = str
    }
 
    //只有内联函数才可以被具体化
    inline fun setArrayStr(array:Array) {
        var str:String = "数组元素依次排列:"
        for (item in array) {
            str = str + item.toString() + ", "
        }
        tv_function_result.text = str
    }

3.扩展函数

系统自带的类已经提供了许多方法,然而经常还是无法完全满足业务需求,此时开发者往往要写个工具类,比如StringUtil、DateUtil之类,来补充相关的处理功能,长此以往,工具类越来越多也越来越难以管理。
基于以上情况,Kotlin推出了扩展函数的概念,允许开发者给系统类补写新的方法,而无需另外编写额外的工具类。比如系统自带的数组Array提供了求最大值的max方法,提供了进行排序的sort方法,可是并未提供交换数组元素的方法。于是我们打算给Array增加新的交换方法,也就是添加一个扩展函数swap,与众不同的是要在函数名称前面加上“Array.”,表示该函数扩展自Array。swap函数的定义代码如下所示:
fun Array.swap(pos1:Int, pos2:Int){
         val temp = this[pos1]
         this[pos1] = this[pos2]
         this[pos2] = temp
}
与泛型函数结合可以增强它的泛用性
fun Array.swap(pos1:Int, pos2:Int){
         val temp = this[pos1]
         this[pos1] = this[pos2]
         this[pos2] = temp
}
实例:消除重复元素
fun main(args: Array) {
    var  city :MutableList = mutableListOf( "Shanghai Job Offered", "Beijing Events", "Beijing Language","Beijing Massage & Escort" ,"Beijing Language","Beijing Events","Shanghai Job Offered")
    var  testNum :MutableList = mutableListOf( 1,2,3,4,1,2,3,4,5)
    testNum.sort()
    for(item in testNum.deleteDuplication()){
        println("$item")
    }
    city.sort()
    for(item in city.deleteDuplication()){
        println("$item")
    }
}

//新建辅助数组
fun MutableList.deleteDuplication():MutableList{
    var res:MutableList = mutableListOf()
    for(i in this.indices){
        if(i>0 && this[i]==this[i-1]) continue
        else res.add(this[i])
    }
    return res
}

4.尾递归函数

Kotlin引入了扩展函数,还能反过来精简函数。具体地说,如果一个函数的表达式比较简单,一两行就可以搞定的话,Kotlin允许使用等号代替大括号。
例如:5!=5*4*3*2*1
fun factorial(n:Int):Int {
    if (n <= 1) n
    else n*factorial(n-1)
}
可以简化成:
fun factorial(n:Int):Int = if (n <= 1) n else n*factorial(n-1)

Kotlin体系还存在一种特殊的递归函数,名叫尾递归函数,它指的是函数末尾的返回值重复调用了自身函数。此时要在fun前面加上关键字tailrec,告诉编译器这是个尾递归函数,则编译器会相应进行优化,从而提高程序性能。
//如果函数尾部递归调用自身,则可加上关键字tailrec表示这是个尾递归函数,
//此时编译器会自动优化递归,即用循环方式代替递归,从而避免栈溢出的情况。
//比如下面这个求余弦不动点的函数就是尾递归函数
tailrec fun findFixPoint(x: Double = 1.0): Double
        = if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))

实例:

5.高阶函数

前面多次提到函数被Kotlin当作特殊变量,包括函数声明采取跟变量声明一样的形式“名称:类型”,以及简化函数允许直接用等号连接函数体等等,那么本节最后讲述的则是把A函数作为B函数的输入参数,就像普通变量一样参与B函数的表达式计算。此时因为B函数的入参内嵌了A函数,故而B函数被称作高阶函数,对应的A函数则为低阶函数。
实例:
//允许将函数表达式作为输入参数传进来,就形成了高阶函数,这里的greater函数就像是个变量
fun maxCustom(array: Array, greater: (T, T) -> Boolean): T? {
    var max: T? = null
    for (item in array)
        if (max == null || greater(item, max))
            max = item
    return max
}
fun main(args: Array){
    var string_array:Array = arrayOf("How", "do", "you", "do", "I'm   ", "Fine")     
    println("字符串数组的默认最大值为${string_array.max()}")
    //因为高阶函数maxCustom同时也是泛型函数,所以要在函数名称后面加上
    println("字符串数组按长度比较的最大值为${maxCustom(string_array, { a, b -> a.length > b.length })}")
    //string_array.max()对应的高阶函数是maxCustom(string_array, { a, b -> a > b })
    println("字符串数组的默认最大值(使用高阶函数)为${maxCustom(string_array, { a, b -> a > b })}")
    //因为系统可以根据string_array判断泛型函数采用了String类型,故而函数名称后面的也可以省略掉
    println("字符串数组按去掉空格再比较长度的最大值为${maxCustom(string_array, { a, b -> a.trim().length > b.trim().length })}")
}

前述的高阶函数maxCustom同时结合了泛型函数的写法,其实还可以给它加上扩展函数的功能。
fun Array.maxCustom(array: Array, greater: (T, T) -> Boolean): T? {
    var max: T? = null
    for (item in array)
        if (max == null || greater(item, max))
            max = item
    return max
}

你可能感兴趣的:(kotlin学习笔记(5)--泛型和高级函数的应用)