美文网首页
Kotlin学习(3)函数的定义和调用

Kotlin学习(3)函数的定义和调用

作者: m1Ku | 来源:发表于2019-07-15 17:36 被阅读0次

    3.1 命名参数和默认参数

    val strings = listOf("1", "2", "3", "4", "5")
     println(strings)
    >> [1, 2, 3, 4, 5]
    

    上面一段代码打印了集合strings中的元素,kotlin对打印api做了处理,结果看起来很友好。那么如果我们要自定义打印的格式该怎么办?当然是写一个方法并对集合循环拼接打印,如下代码段

    fun <T>printStrings(prefix: String, suffix: String, split: String, strings: List<T>) {
    
        val sb = StringBuilder(prefix)
        //带下标的for循环
        for ((index, element) in strings.withIndex()) {
            if (index > 0)
                sb.append(split)
            sb.append(element)
        }
        sb.append(suffix)
        println(sb)
    }
    printStrings("{", "}", ";", strings)
    >>{1;2;3;4;5}
    

    上面功能是实现了,但我们发现还是有问题的。

    • 首先,调用者往往不知道具体参数的含义,用kotlin中的命名参数即可解决。

      //使用命名参数指定函数含义(即方法的形参名)
      printStrings("{",suffix = "}",split = ".",strings = strings)
      

      命名参数的使用

      规则:如果在掉用一个参数时,显示的指明了一个参数的名称,为了避免混淆,那它之后的所有参数都要标明名称。

    • 再次,我们调用函数时往往并不需要指定每个参数的值。在java中我们可以写一大堆重载函数解决,工作繁琐而重复。现在kotlin中有了更好的方式,那就是使用命名参数和默认参数来优化函数的调用。如下代码

    //为 prefix suffix split 均指定默认值
    fun <T> printStrings2(prefix: String = "{", suffix: String = "}", split: String = "*",strings: List<T>) {
    
        val sb = StringBuilder(prefix)
        for ((index, element) in strings.withIndex()) {
            if (index > 0) {
                sb.append(split)
            }
            sb.append(element)
        }
        sb.append(suffix)
        println(sb.toString())
    }
    

    默认参数值定义

    kotlin在声明函数时,可以为参数赋值,即指定这个参数的默认值。这就叫做默认参数值

    这样做的好处是?还是这个函数,看一下调用方式就知道了

    //使用命名参数指定 集合
    printStrings2(strings = strings)
    //使用命名参数指定 前缀 后缀 以及需要使用的集合
    printStrings2(prefix = "X",suffix = "C",strings = strings)
    

    如上我们在给定了默认参数值后,便可以省略一些参数。具体规则如下

    • 常规方式调用时,必须按照函数声明中定义的参数顺序来给定参数,可以省略排在末位的参数
    • 使用命名参数时,可以省略调中间的参数,并且可以按照任意顺序给定需要的参数

    3.2 顶层函数和属性

    顶层函数

    在安卓开发中,我们会将一些通用的功能提取封装成静态方法写在工具类中,项目中会有不少的xxUtils.java类。而现在在Kotlin中我们不必在写这种工具类了,取而代之的就是用顶层函数,顾名思义就是直接写在kotlin文件中的函数。

    • Kotlin中调用,顶层函数是包内成员,包外访问时只要导入即可

    • Java中调用

      Kotlin文件在编译时,会编译成Kotlin文件名的类,而其顶层函数会编译成这个类的静态函数

    依然以上一节的函数为例,在Java代码中调用方式为

    Fundemo1Kt.printStrings("(",")",";",datas);
    

    Kotlin文件编译生成的类的名字在包名前用以下语句指定

    @file:JvmName("ChangeNameTest")
    package com.m1Ku.kt03
    

    此时Java文件中再调用

    ChangeNameTest.printStrings("(",")",";",datas);
    

    顶层属性

    顶层属性放在Kotlin文件的顶层,可以用来计算函数执行的次数,以及定义常量等等

    以下写法会把一个常量以public static final 的形式暴露给Java

    const val BASE_URL = "www.baidu.com"
    

    3.3 扩展函数和属性

    扩展函数

    扩展函数,顾名思义即可以对类进行扩展,为其添加成员函数。但这个扩展函数是定义在这个类外面的。

    fun Context.toast(msg:String,length:Int = Toast.LENGTH_SHORT) {
        Toast.makeText(this,msg,length).show()
    }
    

    上面代码中,我们为Context定义了一个扩展函数,在Activity中我们便可以直接调用这个弹土司的函数

    toast("扩展函数弹的toast")
    

    定义语法

    把要扩展的类或者接口的名称,放在即将添加的函数的前面,这个类的名称被称为接受者类型;用来调用这个扩展函数的那个对象,称为接受者对象

    扩展函数可以调用类中的其他方法和属性,但是不能访问私有的成员和受保护的成员

    在Java中的调用

    扩展函数定义在文件顶层的,同样会被编译成静态函数。java文件中调用时把接收者对象作为第一个参数传入即可

    
    public class OneActivity extends AppCompatActivity {
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //第一个参数就是接收者对象,上面的扩展函数接受者类型为Context
            MainActivityKt.toast(this,"haha", Toast.LENGTH_LONG);
        }
    }
    
    注意:由于Kotlin把扩展函数编译为静态函数,所以扩展函数是不能被重写的

    扩展属性

    可以为类定义一个扩展属性,并为其提供getter和setter方法,这样我们可以像访问接收者普通成员属性一样访问它

    为String定义属性,并定义getter方法获取字符串的最后一个字符

    val String.lastChar: Char
        get() = this.get(length - 1)
    

    当然定义为var可变类型的属性我们可以为其提供setter方法

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

    3.4 可变参数和中缀调用

    可变参数

    Kotlin中可变参数用vararg修饰符来修饰

    fun talk(vararg values:String){
        
    }
    
    注意:java的可变参数可以传入数组,但是Kotlin中必须显示的解包数组传入,在对应的参数前使用解包操作符*既可完成该操作
    fun main(args: Array<String>) {
        talk(*args)
    }
    

    解构声明

    将一个对象解构成多个变量,这样的形式叫解构声明

    //解构对象
    data class Animal(val type:String = "2",val age:Int = 20)
    val(type,age) = Animal()
    //解构pair
    val (number,name) = 1 to "one"
    //解构list with index
    val strings = listOf("1","3","5","8","9")
        for ((index,element) in strings.withIndex()){
            println("index = $index -> element = $element")
        }
    

    3.5 局部函数

    函数内部定义函数,这样可以解决常见的代码重复问题。在局部函数中,可以访问所在函数所有参数和变量

    如下代码,把检查非空的操作提取成局部函数,节省了重复代码

    fun saveAnimal2Db(animal: Animal) {
        fun validateBeforeSave(value: String, fieldName: String) {
            if (value.isEmpty()) {
                throw IllegalArgumentException("${animal.id}的$fieldName 值为空")
            }
        }
        validateBeforeSave(animal.type, "type")
        validateBeforeSave(animal.height, "height")
        //保存
        println("保存成功")
    }
    

    再结合扩展函数的使用,效果更佳:)

    fun saveAnimal2Db(animal: Animal) {
        animal.validateBeforeSave()
        //保存
        println("保存成功")
    }
    

    相关文章

      网友评论

          本文标题:Kotlin学习(3)函数的定义和调用

          本文链接:https://www.haomeiwen.com/subject/mkxokctx.html