函數
函數聲明
kotlin 中用關鍵字 fun 聲明函數:
fun double(x: Int): Int {
}
函數用法
通過傳統的方法調用函數
// 函數的基本用法
fun double(x:Int):Int
{
return 2 * x
}
class MyClass
{
fun add(x:Int, y:Int):Int
{
return x + y
}
companion object {
fun sub(x:Int, y:Int):Int
{
return x - y
}
}
}
fun main(args: Array<String>)
{
println(double(20))
println(MyClass().add(20,40))
println(MyClass.sub(40,1))
}
中綴符號
支持中綴標記法調用的函數必須滿足如下3個條件:
1. 是成員函數,或是擴展函數
2. 只能有一個參數
3. 使用infix關鍵字聲明
//給 Int 定義一個擴展方法
infix fun String.div(str:String):String
{
return this.replace(str,"")
}
fun main(args: Array<String>)
{
// 使用正常的方式調用div函數
var str = "hello world"
println(str.div("l"))
// 使用中綴表達式調用div函數
println(str div "l")
println(str div "l" div "o")
}
輸出:
參數和返回值
函數參數是用 Pascal 符號定義的 name:type。參數之間用逗號隔開,每個參數必
須指明類型。
fun powerOf(number: Int, exponent: Int) {
…
}
默認參數
函數參數可以設置默認值,當參數被忽略時會使用默認值。這樣相比其他語言可以減
少重載。
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size() ) {
...
}
默認值可以通過在type類型後使用 = 號進行賦值
命名參數
在調用函數時可以參數可以命名。這對於那種有大量參數的函數是很方便的.下面是一個例子:
fun reformat(str: String, normalizeCase: Boolean = true,upperCas
eFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ') {
...
}
我們可以使用默認參數
reformat(str)
然而當調用非默認參數是就需要像下面這樣:
reformat(str, true, true, false, '_')
使用命名參數我們可以讓代碼可讀性更強:
reformat(str,
normalizeCase = true,
uppercaseFirstLetter = true,
divideByCamelHumps = false,
wordSeparator = '_'
)
如果不需要全部參數的話可以這樣:
reformat(str, wordSeparator = '_')
注意,命名參數語法不能夠被用於調用Java函數中,因爲Java的字節碼不能確保方法參數命名的不變性
不帶返回值的參數
如果函數不會返回任何有用值,那麼他的返回類型就是 Unit . Unit 是一個只有唯一值 Unit 的類型.這個值並不需要被直接返回:
un printHello(name: String?): Unit {
if (name != null)
println("Hello ${name}")
else
println("Hi there!")
// `return Unit` or `return` is optional
}
Unit 返回值也可以省略,比如下面這樣:
fun printHello(name: String?) {
...
}
變長參數
函數的參數(通常是最後一個參數)可以用 vararg 修飾符進行標記,標記後,允許給函數傳遞可變長度的參數:
// 如果給可變參數類型的函數傳遞數組,前面需要加*
// 函數的可變參數
fun <T> asList(vararg ts:T):List<T>
{
val result = ArrayList<T>()
for(t in ts)
{
result.add(t)
}
return result
}
fun <T> asList1(vararg ts:T,value1:Int, value2:String):List<T>
{
val result = ArrayList<T>()
for(t in ts)
{
result.add(t)
}
println("value1 = ${value1} value2 = ${value2}")
return result
}
fun main(args: Array<String>)
{
var list = asList(1,2,3,"abc",true,30.2)
println(list.toString())
var list1 = asList<Int>(1,2,3)
println(list1.toString())
var list2 = asList1(1,2,4,5,value1 = 20, value2="hello")
val arr = arrayOf(1,2,3,4,5,6,7,8,9,10)
var list3 = asList(*arr)
println(list3.toString() )
println(list3.size )
}
輸出:
只有一個參數可以被標註爲 vararg 。加入 vararg 並不是列表中的最後一個參數,那麼後面的參數需要通過命名參數語法進行傳值,再或者如果這個參數是函數類型,就需要通過lambda法則.當調用變長參數的函數時,我們可以一個一個的傳遞參數,比如 asList(1, 2,3) ,或者我們要傳遞一個 array 的內容給函數,我們就可以使用 * 前綴操作符。
單表達式函數
當函數只返回單個表達式
時,大括號可以省略並在 = 後面定義函數體
fun double(x: Int): Int = x*2
在編譯器可以推斷出返回值類型的時候,返回值的類型可以省略:
fun double(x: Int) = x * 2
明確返回類型
下面的例子中必須有明確返回類型,除非他是返回 Unit 類型的值,Kotlin 並不會對函數體重的返回類型進行推斷,因爲函數體中可能有複雜的控制流,他的返回類型未必對讀者可見(甚至對編譯器而言也有可能是不可見的):
函數範圍
Kotlin 中可以在文件頂級聲明函數,這就意味者你不用像在Java,C#或是Scala一樣創建一個類來持有函數。除了頂級函數,Kotlin 函數可以聲明爲局部的,作爲成員函數或擴展函數。
局部函數
Kotlin 支持局部函數,比如在一個函數包含另一函數。
fun dfs(graph: Graph) {
fun dfs(current: Vertex, visited: Set<Vertex>) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v, visited)
}
dfs(graph.vertices[0], HashSet())
}
局部函數可以訪問外部函數的局部變量(比如閉包)
fun dfs(graph: Graph) {
val visited = HashSet<Vertex>()
fun dfs(current: Vertex) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}
dfs(graph.vertices[0])
}
局部函數甚至可以返回到外部函數 qualified return expressions
fun reachable(from: Vertex, to: Vertex): Boolean {
val visited = HashSet<Vertex>()
fun dfs(current: Vertex) {
if (current == to) return@reachable true
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}
dfs(from)
return false
}
成員函數
成員函數是定義在一個類或對象裏邊的
class Sample() {
fun foo() { print("Foo") }
}
成員函數可以用 . 的方式調用
Sample.foo()
泛型函數
函數可以有泛型參數,樣式是在函數後跟上尖括號。
fun sigletonArray<T>(item: T): Array<T> {
return Array<T>(1, {item})
}
尾遞歸函數
Kotlin 支持函數式編程的尾遞歸。這個允許一些算法可以通過循環而不是遞歸解決問題,從而避免了棧溢出。當函數被標記爲 tailrec 時,編譯器會優化遞歸,並用高效迅速的循環代替它。tailrec fun findFixPoint(x: Double = 1.0): Double
= if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))這段代碼計算的是數學上的餘弦不動點。Math.cos 從 1.0 開始不斷重複,直到值不變爲止,結果是 0.7390851332151607 這段代碼和下面的是等效的:
private fun findFixPoint(): Double {
var x = 1.0
while (true) {
val y = Math.cos(x)
if ( x == y ) return y
x = y
}
}
使用 tailrec 修飾符必須在最後一個操作中調用自己。在遞歸調用代碼後面是不
允許有其它代碼的,並且也不可以在 try/catch/finall 塊中進行使用。當前的尾遞歸
只在 JVM 的後端中可以用