Kotlin系列之命名引數和預設引數
今天一起來看看Kotlin的命名引數和預設引數。
如果你學過Python,那你對這兩個概念一定不陌生,那我們今天就來學習一波Kotlin中的預設引數和命名引數。
遇到的問題
為了說明命名引數和預設引數出現的必要性,我們先丟擲一個問題,如何打印出一個集合,並且定製它的列印形式。在Java中我們最常用的思路就是重寫toString()方法或者是寫一個列印集合的工具類,比如下面的Java程式碼。
Java程式碼
public class Main {
public static void main(String[] args) {
List<String> mArrayList = new ArrayList<String>(){
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < this.size(); i++) {
stringBuilder.append(this.get(i));
if (i < this .size() - 1){
stringBuilder.append(";");
}
}
return stringBuilder.toString();
}
};
mArrayList.add("123");
mArrayList.add("456");
mArrayList.add("789");
System.out.println(mArrayList);
}
}
上面我們是重寫toString()方法,那我們看看在Kotlin中怎麼做呢?我們先看看Kotlin中預設的列印樣式。
Kotlin程式碼
fun main(args: Array<String>){
val list = listOf<String>("123", "456", "789")
println(list)
}
//列印結果:[123, 456, 789]
我們現在就先用Kotlin來寫一個定製列印集合樣式的函式。
Kotlin程式碼
fun <T> joinToString(collection: Collection<T>, separator: String,
prefix: String, suffix: String): String{
val result = StringBuilder(prefix)
for ((index, element) in collection.withIndex()){
if (index > 0){
result.append(separator)
}
result.append(element)
}
result.append(suffix)
return result.toString();
}
上面這種方法就是採用自己定義一個工具類,通過傳入不同的引數來定製列印的樣式。
命名引數
我們這裡先來看看如何呼叫上面的函式,再說說存在的問題。
Kotlin程式碼
println(joinToString(list, ";", "(", ")"))
顯然這裡有一個很不方便的地方,如果我們不借助於IDE的提示和檢視方法定義,我們是很難看懂我們傳遞給函式的每個引數的具體含義,所以這個時候命名引數
出現了。看這個名字就能知道,它是給引數命名,讓我們對引數的傳入和檢視更加清楚,就像下面這樣。
Kotlin程式碼
println(joinToString(list, ";", "(", ")"))
println(joinToString(collection = list, separator = ";", prefix = "(", suffix = ")"))
println(joinToString( separator = ";", collection = list, prefix = "(", suffix = ")"))
看到了嗎,我們給傳入的每個引數前面加了一個名稱,這樣是不是很直觀呢,而且由於這裡的引數是用命名指定的,所以引數的順序可以和定義函式的順序不同,上面的三種方式都是可以正確呼叫的。
但是這裡有一些注意事項:
1. 如果我們已經指定了一個引數的名稱,那麼這個引數其後的引數也必須指定名稱,不讓就會造成混淆,Kotlin編譯器就不能明白你的意圖。
2. 命名引數的形式只能用於Kotlin的函式,不能用於Java的函式,就像下面這樣是錯誤的。
//Java程式碼
public class Main {
public static void javaFun(String name){
System.out.println(name);
}
}
//Kotlin程式碼
fun main(args: Array<String>){
//會報錯[命名引數不能用於Java函式]
println(Main.javaFun(name = "bingjianit"))
}
預設引數
我們再回到文章開頭的那個需求,我們希望我們使用Kotlin寫的函式,可以在我們不傳入分隔符是,預設使用”,”,在我們傳入分隔符時使用我們傳入的分隔符,也就是讓我們的函式的某些引數具有預設值。其實同樣的需求我們在Java中可以通過過載函式來實現,但是那會造成會出現很多個過載函式,在Kotlin中,我們可以通過預設引數來實現這個需求。
Kotlin程式碼
fun <T> joinToString2(collection: Collection<T>, separator: String = ",",
prefix: String = "[", suffix: String = "]"): String{
val result = StringBuilder(prefix)
for ((index, element) in collection.withIndex()){
if (index > 0){
result.append(separator)
}
result.append(element)
}
result.append(suffix)
return result.toString();
}
上面的程式碼中我們為後面三個引數指定了預設值,當我們不傳入值時,使用預設值,傳入值時使用我們自己的引數,我們可以像下面一樣呼叫。
Kotlin程式碼
//輸出 [123,456,789]
println(joinToString2(list))
//輸出 [123;456;789]
println(joinToString2(list, separator = ";"))
可以看出,我們這裡其實是結合使用了預設引數和命名引數。
由於Kotlin與Java可以互操作,那麼上面的函式在Java中怎麼呼叫呢?由於Java中不支援預設引數,所以我們必須為函式傳入每一個引數。就像下面這樣:
我們在Str.kt
檔案中寫了我們上面定義的有預設值的函式
//在Java中呼叫[必須傳入全部的引數]
System.out.println(StrKt.joinToString2(mArrayList, ",", "{", "}"));
當然,如果你不想寫這麼繁瑣,可以在Kotlin預設函式生成時新增一個@JvmOverloads註解,編譯器就會自動為每個預設引數生成過載函式,就像下面這樣
Kotlin程式碼
@JvmOverloads
fun <T> joinToString2(collection: Collection<T>, separator: String = ",",
prefix: String = "[", suffix: String = "]"): String{
val result = StringBuilder(prefix)
for ((index, element) in collection.withIndex()){
if (index > 0){
result.append(separator)
}
result.append(element)
}
result.append(suffix)
return result.toString();
}
生成的Java過載函式如下
Java程式碼
public static <T> T joinToString2(Collection<T> collection);
public static <T> T joinToString2(Collection<T> collection, String separator);
public static <T> T joinToString2(Collection<T> collection, String separator, String prefix);
public static <T> T joinToString2(Collection<T> collection, String separator, String prefix, String suffix);
這樣我們在呼叫時就不需要傳那麼多引數了。
寫在最後
Kotlin結合了多種語言的優秀特性,變成了一門簡潔、精緻的程式語言,並且還具備了和Java無縫的互操作性。