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

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("保存成功")
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容