Kotlin從零開始:Packages包和基本功能
Chinese (Traditional) (中文(繁體)) translation by Zhang Xiang Liang (you can also view the original English article)
Kotlin是壹種編譯為Java字節碼的現代編程語言。它是免費的,開源的,並有望使Android開發更有趣。
在前面的文章中,您了解了Kotlin範圍和集合。在本教程中,我們將繼續通過查看如何使用包來組織代碼來學習該語言,然後繼續介紹Kotlin中的函數。
1. Packages包
如果您熟悉Java,那麽您知道Java使用包對相關類進行分組; 例如, java.util Packages包有許多有用的實用程序類。包是用package關鍵字聲明的,任何帶有package聲明的Kotlin文件可以包含類,函數或接口的聲明。
聲明
查看下面的代碼,我們已經使用package關鍵字聲明了壹個包com.chikekotlin.projectx。另外,我們在這個包中聲明了壹個類MyClass(我們將在Kotlin中討論未來職位中的類)。
1 |
package com.chikekotlin.projectx |
2 |
|
3 |
class MyClass |
現在,該類的完全限定名稱MyClass是com.chikekotlin.projectx.MyClass。
1 |
package com.chikekotlin.projectx |
2 |
|
3 |
fun saySomething(): String { |
4 |
return "How far?" |
5 |
}
|
在上面的代碼中,我們創建了壹個頂級函數(我們很快就會談到)。與MyClass類似 ,saySomething()函數的完全限定名稱是com.chikekotlin.projectx.saySomething。
imports
在Kotlin中,我們使用import聲明來使編譯器能夠定位要導入的類,函數,接口或對象。另壹方面,在Java中,我們不能直接導入函數或僅用於方法的類或接口。
我們import用來訪問函數,接口,類或對象聲明的包之外。
1 |
import com.chikekotlin.projectx.saySomething |
2 |
|
3 |
fun main(args: Array<String>) { |
4 |
saySomething() // will print "How far?" |
5 |
}
|
在上面的代碼片段中,我們 從不同的包中導入了函數saySomething() ,然後我們執行了該函數。
Kotlin還支持使用*運算符的通配符導入。這將壹次性導入包中聲明的所有類,接口和函數。不過不建議這樣做 - 通常情況下,要明確導入導入會更好。
1 |
import com.chikekotlin.projectx.* |
導入別名
如果庫的類名或函數名稱有沖突(例如,它們都聲明壹個具有相同名稱的函數),則可以使用as關鍵字為該導入的實體指定壹個臨時名稱。
1 |
import com.chikekotlin.projectx.saySomething |
2 |
import com.chikekotlin.projecty.saySomething as projectYSaySomething |
3 |
|
4 |
fun main(args: Array<String>) { |
5 |
projectYSaySomething() |
6 |
}
|
請註意,臨時名稱僅在分配文件的位置使用。
2.函數
壹個函數將壹系列執行任務的代碼語句分組在壹起。函數的實現細節對調用者是隱藏的。
在Kotlin中,函數是使用fun關鍵字定義的,如下例所示:
1 |
fun hello(name: String): String { |
2 |
return "Hello $name" |
3 |
}
|
4 |
val message = hello("Chike") |
5 |
print(message) // will print "Hello Chike" |
在上面的代碼中,我們使用單個name的String類型參數定義了壹個簡單的函數hello()。 這個函數返回壹個String類型。 對功能參數定義格式name: type,例如 age: Int,price: Double,student:
1 |
fun hello(name: String): Unit { |
2 |
print("Hello $name") |
3 |
}
|
4 |
|
5 |
hello("Chike") // will print "Hello Chike" |
上面的函數與前壹個類似,但請註意這個函數的返回類型為Unit。因為這個函數不會給我們返回任何重要的值 - 它只是打印出壹條消息 - 它的返回類型是Unit默認的。 Unit是壹個Kotlin對象(我們將在稍後的帖子中討論Kotlin對象),它類似於VoidJava和C中的Void類型。
1 |
public object Unit { |
2 |
override fun toString() = "kotlin.Unit" |
3 |
}
|
請註意,如果您沒有顯式聲明返回類型為 Unit,則該類型由編譯器推斷。
1 |
fun hello(name: String) { // will still compile |
2 |
print("Hello $name") |
3 |
}
|
單行功能
單行或單行函數是只有單個表達式的函數。在這個函數中,我們擺脫大括號並 在表達式之前使用符號=。換句話說,我們擺脫了函數塊。
1 |
fun calCircumference(radius: Double): Double { |
2 |
return (2 * Math.PI) * radius |
3 |
}
|
上面的功能可以縮短為壹行:
1 |
fun calCircumference(radius: Double) = (2 * Math.PI) * radius |
查看上面更新的函數,可以看到我們通過刪除大括號{},return關鍵字以及返回類型(由編譯器推斷)來使代碼更加簡潔。
如果需要,仍然可以包含返回類型以更明確。
1 |
fun calCircumference(radius: Double): Double = (2 * Math.PI) * radius |
命名參數
命名參數通過命名被調用時傳遞給函數的參數來提供更多可讀函數。
在下面的例子中,我們創建了壹個打印我的全名的函數。
1 |
fun sayMyFullName(firstName: String, lastName: String, middleName: String): Unit { |
2 |
print("My full name is $firstName $middleName $lastName"); |
3 |
}
|
要執行上面的函數,我們可以這樣調用它:
1 |
sayMyFullName("Chike", "Nnamdi", "Mgbemena") |
看看上面的函數調用,我們不知道哪些String類型參數等同於哪些函數參數(盡管壹些IDE,比如IntelliJ IDEA可以幫助我們)。函數的用戶必須查看函數簽名(或源代碼)或文檔以了解每個參數對應的內容。
1 |
sayMyFullName(firstName = "Chike", middleName = "Nnamdi", lastName = "Mgbemena") |
在上面的第二個函數調用中,我們在參數值之前提供了參數名稱。妳可以看到這個函數調用比前壹個更清晰,更具可讀性。 這種調用函數的方式有助於減少錯誤更換相同類型的參數時可能發生的錯誤。
調用者還可以使用命名參數更改參數的順序。例如:
1 |
sayMyFullName(lastName = "Mgbemena", middleName = "Nnamdi", firstName = "Chike") // will still compile |
在上面的代碼中,我們將參數的位置firstName與lastName。參數順序與命名參數無關,因為編譯器會將它們中的每壹個映射到正確的函數參數。
默認參數
在Kotlin中,我們可以為其任何參數提供壹個函數默認值。如果在函數調用期間沒有為參數分配任何參數,則使用這些默認值。要在Java中執行此操作,我們必須創建不同的重載方法。
在這裏,在我們的calCircumference()方法中,我們通過為pi參數添加壹個默認值來修改該方法Math.PI,這是java.lang.Math包中的壹個常量 。
1 |
fun calCircumference(radius: Double, pi: Double = Math.PI): Double = (2 * pi) * radius |
當我們調用這個函數時,我們可以傳遞我們的近似值pi 或使用默認值。
1 |
print(calCircumference(24.0)) // used default value for PI and prints 150.79644737231007 |
2 |
print(calCircumference(24.0, 3.14)) // passed value for PI and prints 150.72 |
我們來看另壹個例子。
1 |
fun printName(firstName: String, middleName: String = "N/A", lastName: String) { |
2 |
println("first name: $firstName - middle name: $middleName - last name: $lastName") |
3 |
}
|
在下面的代碼中,我們嘗試調用該函數,但它不會編譯:
1 |
printName("Chike", "Mgbemena") // won't compile |
在上面的函數調用中,我將我的名字和姓氏傳遞給函數,並希望使用中間名的默認值。 但是這不會編譯,因為編譯器很困惑。它不知道參數“Mgbemena”是用於參數middleName還是lastName參數?
為了解決這個問題,我們可以結合命名參數和默認參數。
1 |
printName("Chike", lastName = "Mgbemena") // will now compile |
Java互操作性
鑒於Java不支持方法中的默認參數值,因此您需要在從Java調用Kotlin函數時明確指定所有參數值。 但Kotlin為我們提供了通過註釋Kotlin函數使Java調用方便的功能@JvmOverloads。這個註解將指示Kotlin編譯器為我們生成Java重載函數。
在下面的例子中,我們使用了註釋calCirumference()函數@JvmOverloads。
1 |
@JvmOverloads
|
2 |
fun calCircumference(radius: Double, pi: Double = Math.PI): Double = (2 * pi) * radius |
以下代碼由Kotlin編譯器生成,以便Java調用者隨後可以選擇要調用哪壹個。
1 |
// Java
|
2 |
double calCircumference(double radius, double pi); |
3 |
double calCircumference(double radius); |
在上次生成的Java方法定義中,該pi參數被省略。這意味著該方法將使用默認pi值。
無限的參數
在Java中,我們可以創建壹個方法,通過...在方法的參數列表中輸入壹個類型後包含壹個省略號(...)來接收未指定數量的參數。Kotlin函數也使用vararg 修飾符後跟參數名稱來支持此概念 。
1 |
fun printInts(vararg ints: Int): Unit { |
2 |
for (n in ints) { |
3 |
print("$n\t") |
4 |
}
|
5 |
}
|
6 |
printInts(1, 2, 3, 4, 5, 6) // will print 1 2 3 4 5 6 |
vararg 修改允許調用者在逗號分隔的參數列表傳遞。然後,這個參數列表將被包裝到壹個數組中。
當函數有多個參數時,該 vararg 參數通常是最後壹個參數。也可以在參數後面有參數vararg,但在調用函數時需要使用命名參數來指定它們。
1 |
fun printNumbers(myDouble: Double, myFloat: Float, vararg ints: Int) { |
2 |
println(myDouble) |
3 |
println(myFloat) |
4 |
for (n in ints) { |
5 |
print("$n\t") |
6 |
}
|
7 |
}
|
8 |
printNumbers(1.34, 4.4F, 2, 3, 4, 5, 6) // will compile |
例如,在上面的代碼中,帶有vararg修飾符的參數位於多參數列表中的最後壹個位置(這是我們通常所做的)。 但如果我們不想要它在最後的位置呢?在下面的例子中,它處於第二個位置。
1 |
fun printNumbers(myDouble: Double, vararg ints: Int, myFloat: Float) { |
2 |
println(myDouble) |
3 |
println(myFloat) |
4 |
for (n in ints) { |
5 |
print("$n\t") |
6 |
}
|
7 |
}
|
8 |
printNumbers(1.34, 2, 3, 4, 5, 6, myFloat = 4.4F) // will compile |
9 |
printNumbers(1.34, ints = 2, 3, 4, 5, 6, myFloat = 4.4F) // will not compile |
10 |
printNumbers(myDouble = 1.34, ints = 2, 3, 4, 5, 6, myFloat = 4.4F) // will also not |
11 |
compile
|
正如妳可以在上面更新的代碼中看到的,我們使用最後壹個參數的命名參數來解決這個問題。
Spread運算符
假設我們想要將壹個整數數組傳遞給我們的printNumbers() 函數。不過,該函數期望將這些值展開到參數列表中。 如果妳試圖直接傳入數組給printNumbers(),妳會發現它不會被編譯。
1 |
val intsArray: IntArray = intArrayOf(1, 3, 4, 5) |
2 |
printNumbers(1.34, intsArray, myFloat = 4.4F) // won't compile |
為了解決這個問題,我們需要使用spread運算符。這個運算符將解壓該數組,然後將各個元素作為參數傳遞給我們的函數。
1 |
val intsArray: IntArray = intArrayOf(1, 3, 4, 5) |
2 |
printNumbers(1.34, *intsArray, myFloat = 4.4F) // will now compile |
為了解決這個問題,我們需要使用spread 運算符*。這個運算符將解壓該數組,然後將各個元素作為參數傳遞給我們的函數。 通過插入參數列表裏面的intsArray前的spread運算符*,現在該代碼編譯並產生相同的結果,如果我們通過的元素intsArray為逗號分隔的參數列表。
返回多個值
有時我們想從壹個函數返回多個值。壹種方法是使用Kotlin中的Pair類型創建Pair並返回它。 該Pair結構包含了以後可以訪問的兩個值。這Kotlin類型可以接受妳提供它的構造函數的任何類型。而且,更重要的是,這兩種類型甚至不需要相同。
1 |
fun getUserNameAndState(id: Int): Pair<String?, String?> { |
2 |
require(id > 0, { "Error: id is less than 0" }) |
3 |
|
4 |
val userNames: Map<Int, String> = mapOf(101 to "Chike", 102 to "Segun", 104 to "Jane") |
5 |
val userStates: Map<Int, String> = mapOf(101 to "Lagos", 102 to "Imo", 104 to "Enugu") |
6 |
|
7 |
val userName = userNames[id] |
8 |
val userState = userStates[id] |
9 |
return Pair(userName, userState) |
10 |
}
|
在上面的功能,我們構建了新 Pair 通過傳遞 userName 和 userState 變量分別作為第壹和第二參數給它的構造,然後回到該 Pair 給調用者。
另外需要註意的是,我們使用了函數中調用require()的getUserNameAndState()函數。 標準庫中的這個輔助函數用於為我們的函數調用者提供壹個滿足的前提條件,否則IllegalArgumentException 就會拋出壹個函數 (我們將在未來的文章中討論Kotlin中的Exceptions)。 可選的第二個參數require() 是壹個函數文字,如果拋出異常,則返回要顯示的消息。例如,調用 getUserNameAndState()函數並將其-1作為參數傳遞給它將觸發:



從Pair中檢索數據
1 |
val userNameAndStatePair: Pair<String?, String?> = getUserNameAndState(101) |
2 |
println(userNameAndStatePair.first) // Chike |
3 |
println(userNameAndStatePair.second) // Lagos |
在上面的代碼中,我們訪問Pair從所述第壹和第二值通過使用它的類型 first和second性質。
但是,這樣做有壹個更好的方法:解構。
1 |
val (name, state) = getUserNameAndState(101) |
2 |
println(name) // Chike |
3 |
println(state) // Lagos |
我們在上面更新的代碼中所做的是直接將返回Pair類型的第壹個和第二個值 分別賦值給變量name和變量state。這個特性被稱為解構聲明。
返回三個值或更多值
現在,如果妳想要壹次返回三個值呢?Kotlin為我們提供了另壹種有用的類型 Triple。
1 |
fun getUserNameStateAndAge(id: Int): Triple<String?, String?, Int> { |
2 |
require(id > 0, { "id is less than 0" }) |
3 |
|
4 |
val userNames: Map<Int, String> = mapOf(101 to "Chike", 102 to "Segun", 104 to "Jane") |
5 |
val userStates: Map<Int, String> = mapOf(101 to "Lagos", 102 to "Imo", 104 to "Enugu") |
6 |
|
7 |
val userName = userNames[id] |
8 |
val userState = userStates[id] |
9 |
val userAge = 6 |
10 |
return Triple(userNames[id], userStates[id], userAge) |
11 |
}
|
12 |
|
13 |
val (name, state, age) = getUserNameStateAndAge(101) |
14 |
println(name) // Chike |
15 |
println(state) // Lagos |
16 |
println(age) // 6 |
我相信妳們中有些人想知道如果妳想返回三個以上的值,該怎麽辦。當我們討論Kotlin的數據類時,答案將在後面的文章中。
結論
在本教程中,您了解了Kotlin編程語言中的軟件包和基本功能。在Kotlin From Scratch系列的下壹篇教程中,您將會更多地了解Kotlin的功能。再見!
要了解有關Kotlin語言的更多信息,我建議訪問Kotlin文檔。或者在Envato Tuts +上查看我們的其他壹些Android應用程序開發帖子!













