天天看點

Kotlin(五)、函數定義與調用

一、為了更友善調用

一個函數定義如下:

/**
     * 實作輸入一個集合,如1,2,3 通過這個方法列印出 (1;2;3)
     */
    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()
    }
           

調用:

joinToString(collection, ";", "(", ")")

命名參數

可以發現調用時可讀性非常的差,如果不去查源碼函數聲明很難确定每個參數的含義,對于很多個Boolean類型的參數的函數更為明顯。

未解決這一問題,Kotlin可以顯式的标明參數名稱

joinToString(collection, separator = ";", prefix = "(", postfix = ")")

注意:

  • 重命名函數的參數時要使用Rename處理,不能手動修改。
  • 調用Java函數時不能采用命名參數的方式
預設參數值

Java中一個普遍存在的問題是,一些類的重載方法是在是太多了。為了相容性,導緻重複。

在Kotlin中可以在聲明函數時指定參數的預設值,這樣可以避免一部分的重複。

改進一下之前的函數

fun <T> joinToString(collection: Collection<T>,//輸入集合
                         separator: String = ";",//分隔符
                         prefix: String = "(",//字首
                         postfix: String = ")"//字尾
    ): String
           

調用結果

joinToString(collection, ";", "(", ")")
 joinToString(collection, ";", "(")
 joinToString(collection, ";")
 joinToString(collection)
 //上面幾種方式結果相同
           
消除頂層

如Java中的Util類,其中有靜态變量和靜态方法,使用類名點調用。

Kotlin中不需要在檔案中建立了類,直接寫方法或靜态變量即可

二、擴充函數和屬性

Kotlin可以為任何類添加成員函數

fun String.qit(): String return "1"
           

為String這個類添加了qit()函數,之後任何String類型的變量都會擁有這個函數

導入擴充函數

對于一個定義的擴充函數,他并不會自動的在整個項目中生效。為了避免偶然性的命名沖突,和其他函數一樣,使用它需要進行導入。

import 包名.qit

//也可用*
import 包名.*

//還可以使用as來修改名稱
import 包名.qit as tian
val c = "x".tian()
           

關鍵字as是解決命名沖突的唯一方式

注意:

  • 擴充函數相當于靜态函數,不能被重寫
擴充屬性
//聲明一個擴充屬性
val String.mVal: String
    get() = "2"

//聲明一個可變的擴充屬性
var StringBuilder.mVar: String
    get() = "2"
    set(value) {
        this.setCharAt(1, '1')
    }
           

三、處理集合:可變參數、中綴調用和庫的支援

可變參數
val list = listOf(1, 2, 3)
//可以發現建立List時可以傳任意數量的參數進去
//函數在庫中的聲明如下
fun <T> listOf(vararg value: T): List<T> {
	...
}
           

vararg修飾符如同Java中三個點,不同的是當傳入的參數包裝在數組中時,Java可以直接傳入數組,而Kotlin要求解包裝,隻需在數組前加

*

即可

fun main(args:Array<String>){
    val list = listOf("string1",*args)
}
           
中綴調用
val map = mapOf(1 to "one", 2 to "two", 3 to "three")
           

這行代碼中的

to

是一種特殊函數調用,叫中綴調用,放在目标對象名稱和參數之間。中綴調用可以與隻有一個參數的函數一起使用,無論是普通函數還是擴充函數。要允許使用中綴符号調用函數,需要使用infix修飾符标記。

下面是一個簡版的to函數聲明:

infix fun Any.to(other: Any) = Pair(this, other)//忽略泛型
           

使用

to

可以生成

Pair對象

val (number, name) = 1 to "one"//解構聲明
           

四、局部函數和擴充

為減少重複代碼,Kotlin可以再函數中聲明嵌套的函數

class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) {
    if (user.name.isEmpty()) throw IllegalArgumentException("Can't save user ${user.id}: empty Name")
    if (user.name.isEmpty()) throw IllegalArgumentException("Can't save user ${user.id}: empty Address")

}
           

這段代碼中重複代碼很少,但如果字段很多而且全面的驗證每一個字段的特殊情況,那就很麻煩了,使用局部函數之後可以這樣:

class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) {
    fun validate(user: User, value: String, fieldName: String) {
        if (value.isEmpty()) throw IllegalArgumentException("Can't save user ${user.id}: empty $fieldName")
    }
    validate(user,user.name,"Name")
    validate(user,user.address,"Address")
}
           

因為局部函數可以通路函數中所有變量,是以可以去掉局部的User參數

class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) {
    fun validate(value: String, fieldName: String) {
        if (value.isEmpty()) throw IllegalArgumentException("Can't save user ${user.id}: empty $fieldName")
    }
    validate(user.name, "Name")
    validate(user.address, "Address")
}
           

提取成為User的擴充函數

class User(val id: Int, val name: String, val address: String)

fun User.validateBeforeSave() {
    fun validate(value: String, fieldName: String) {
        if (value.isEmpty()) throw IllegalArgumentException("Can't save user $id: empty $fieldName")
    }
    validate(name, "Name")
    validate(address, "Address")
}

fun saveUser(user: User) {
    user.validateBeforeSave()
}