函式引數之惑
當一個函式擁有多個引數,且存在多個相同型別引數緊挨著的情況時,往往不太清楚傳入引數的位置是否正確,且嚴重影響函式的可讀性。需要呼叫者跳轉到函式對應的地方,對引數和函式定義中的引數列表進行匹對。這將對函式呼叫者造成很大的麻煩和困擾。
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()
}
複製程式碼
針對這種情況,或許可以依靠IDE進行優化。如:Idea早已對此進行了優化,在對函式填寫引數時,會將引數對應位置的引數名稱進行提示。
命名引數
Kotlin在語法層上對該情況進行優化,當呼叫一個Kotlin定義的函式時,可以顯示標明引數的名稱。這種引數叫命名引數。
當指明一個引數的名稱後,避免混淆,其他引數都要標明名稱。(既然顯式的標明名稱,也就不需要按原本引數定義的順序傳入引數)
joinToString(array,prefix = "(",separator = ",", postfix = "]")
複製程式碼
注意:
- 既然顯示的標明瞭引數名,也就意味著當引數名或方法名進行改變時,其顯式標明的引數名或方法名也需要進行改變。這時可以使用Idea的Rename進行修改。(先選中方法名或引數名 -> Refactor -> Rename)
- 當呼叫Java定義的函式時,不能採用命名引數,因為Java8之前不能把引數名存在,class檔案中。而Kotlin需要與Java6相容。所以,編譯器不能識別出函式引數的名稱。
函式過載之禍
在Java中,支援對函式進行過載。這就造成多個相同名稱的函式,且其引數間只有細微的差別。當呼叫省略部分引數的函式時,可能不清楚到底呼叫的是哪一個函式。(例如:Thread類擁有8個建構函式)
預設引數
而Kotlin只需要指定引數的預設值,就可以有效避免建立多個過載函式。這種帶有預設值的函式引數叫做預設引數。再配合命名引數進行使用時,可以很方便的對指定引數進行賦值,從而實現過載。
fun <T> joinToString(collection: Collection<T>,
separator:String = ",",
prefix:String = "",
postfix:String = ""):String
複製程式碼
呼叫時只需要傳入具體的集合物件,函式會使用預設引數的預設值對其進行運算。
當然,按引數定義的順序,傳入對應的引數也完全沒有問題。
val string = joinToString(array)
//像以前傳遞字首,分割符和字尾也沒有問題
val string = joinToString(array,"(",",")
val string = joinToString(array,"(",",", "]")
//配合命名引數食用,效果更佳
val string = joinToString(array,separator = ";")
複製程式碼
@JvmOverloads 提高Kotlin與Java的互動性
Java 中沒有預設引數的概念,當從Java中呼叫Kotlin的函式時,必須顯示地傳遞所有引數值。為了讓Java呼叫者能呼叫該方法的過載函式,可以用@JvmOverloads註解它。在編譯時,編譯器會從最後一個引數開始逐個省略,生成Java的過載函式。
區域性函式
程式猿都認為方法越小越好,相比縱向冗長的程式碼片段,將其按照職責切分成功能單一的小的區域性方法,最後組織起來呼叫是最好的。但很多時候分解出來的小方法之間並沒有什麼明確的關係,最後以一個包含許多小方法的類告終。
Kotlin中支援區域性函式,所謂區域性函式就是在方法中宣告方法,內部方法可以獲取到外部函式的引數和區域性變數。可以將各個小方法定義為區域性方法,即提供所需的簡潔結構也無須額外的語法開銷。
data class Person(val age:String?,val name:String?)
fun daqi(person: Person){
if (person.age == null){
throw IllegalArgumentException()
}
if (person.name == null){
throw IllegalArgumentException()
}
//正常操作
}
複製程式碼
轉換為區域性函式:
fun daqi(person: Person){
//需要在頂層定義,不然函式未定義無法使用
fun personFileIsEmpty(value:String?,fileName:String){
if (value == null){
throw IllegalArgumentException("$fileName is null")
}
}
personFileIsEmpty(person.age,"age")
personFileIsEmpty(person.name,"name")
//正常操作
}
複製程式碼
區域性函式的缺點是:區域性函式的不能宣告為內聯,並且擁有區域性函式的的函式也不能宣告為內聯。暫時沒有任何辦法避免這種函式的呼叫成本。
參考資料:
- 《Kotlin實戰》
- Kotlin官網