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

2019-07-15  本文已影响0人  m1Ku

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}

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

//为 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文件中的函数。

依然以上一节的函数为例,在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("保存成功")
}
上一篇下一篇

猜你喜欢

热点阅读