摸鱼不如摸Kotlin之函数

941 阅读4分钟

消除静态工具类:顶层函数和属性

Java中的代码都要写到类的函数里面,但很多时候创建的类只是作为静态函数的容器。但在Kotlin中就不需要创建无意义的类,它可以把函数直接放到代码文件的顶层,不用从属于任何的类。

举个栗子,创建一个strings包

在package strings下创建joinToString函数

fun<T> joinToString(collection: Collection<T>,
                    separator: String= "",
                    prefix: String = "",
                    postfix: String = ""): String{

    val result = StringBuilder(prefix)
    for ((index,element) in collection.withIndex()){
        if(index > 0) result.append(separator)
        result.append(element)
    }

    result.append(postfix)
    return result.toString()
}

在kotlin中调用这个函数

fun main() {
    val list = listOf(1,2,3)
    //调用四个参数实现数组元素的拼装
    val res = com.example.kotlinoflearn.function.joinToString(list, ",", "(", ")")
    println(res)
}

结果: (1,2,3)

那么在Java中如何调用这个函数呢?

要调用这个函数必须导入这个包文件。因为这个函数所在的文件名是TopFunction,所以编译器在编译这个文件的时候就会将它编译成TopFunctionK类。并且将joinToString编译成静态的类

import strings.TopFunctionKt;

        Set<Integer> set = new HashSet<>();
        set.add(1);
        set.add(2);
        set.add(3);
        String res = TopFunctionKt.joinToString(set,",","(",")");
        System.out.println(res);

可以看到调用joinToString函数和调用静态函数一样的方式

结果: (1,2,3)

扩展函数

  • 扩展函数 : 一个类的成员函数,不过定义在类的外面
  • 扩展的那个类叫做接收者类型,用来调用这个扩展函数的那个对象叫做接收者对象
  • 注意:扩展函数不允许你打破它的封装性,和类内部定义不一样的是扩展函数不能访问私有的或者受保护的成员

比如上面的joinToString函数就可以改成Collection的扩展函数

fun<T> Collection<T>.joinToString(
                    separator: String= "",
                    prefix: String = "",
                    postfix: String = ""): String{

    val result = StringBuilder(prefix)
    //扩展函数里面this就表示这个类,比如这里this -> Collection
    for ((index,element) in this.withIndex()){
        if(index > 0) result.append(separator)
        result.append(element)
    }

    result.append(postfix)
    return result.toString()
}

注意:重写成员函数是很常见的,但扩展函数是不可以重写的。 对于扩展函数来说,它不是类的一部分,是声明在类之外的。 所以它被调用时,用到哪一个是由变量的静态类型决定的

举个栗子

写个父类View和函数click,子类Button重写click函数

open class View{
    open fun click() = println("View clicked")
}

class Button: View(){
    override fun click() = println("Button clicked")
}

定义一个Button实例,调用click函数

 val v: View = Button()
 println(v.click())  // Button clicked

结果:Button clicked

发现输出结果是Button clicked,这是子类重写父类click函数的结果

那如果为这两个类添加扩展函数,会是什么样的结果呢?

添加showOff扩展函数

fun View.showOff() = println(" I am a view!")
fun Button.showOff() = println("I am a button!")

调用扩展函数

val v: View = Button()
println(v.showOff())

输出结果: I am a view!

验证了之前的结论,扩展函数是不支持重写的。它被调用时,用到哪一个是由变量的静态类型决定的。这里的静态类型是View类型,所以调用的是View的扩展函数showOff。

扩展属性

扩展属性的实现和扩展函数有点相似,但是扩展属性必须写getter方法,因为它没有默认的实现。

  • 如果属性是不可变的,只需要写getter函数,比如String类型。
  • 如果属性是可变的,需要写setter函数和getter函数,比如StringBuilder类型
val String.lastChar: Char
    get() = get(length - 1)

var StringBuilder.lastChar: Char
    get() = get(length - 1)
    set(value: Char){
        this.setCharAt(length - 1,value)
    }

总结

学习Kotlin不仅仅是在学习一种新语言,更是在学习改变习惯思考方式的过程

和Java对比,用Kotin给你带来不一样的思考习惯

  • Kotlin可以给函数参数定义默认值,这大大降低了重载函数的必要性,而且命名参数让多参数函数的调用更加易读。 (具体内容可以传送到笔者之前的文章 Kotlin艺术探索之参数和异常
  • Kotlin可以用扩展函数和属性来扩展任何类的API,包括在外部库中定义的类,而不需要修改其源代码,也没有运行时开销。
  • Kotlin允许更灵活的代码结构:函数和属性都可以在文件中声明,而不仅仅在类中作为成员