1. 程式人生 > >Kotlin系列之命名引數和預設引數

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無縫的互操作性。