Kotlin 的函數定義和使用 (譯文 轉)

Kotlin 的函數定義和使用

函數聲明
Kotlin 中的函數使用 fun 關鍵字聲明算法

fun double(x: Int): Int {
}
函數用法
調用函數使用傳統的方法編程

val result = double(2)
調用成員函數使用點表示法後端

Sample().foo() // 建立類 Sample 實例並調用 foo
中綴表示法
函數還能夠用中綴表示法調用,當數組

他們是成員函數或擴展函數
他們只有一個參數
他們用 infix 關鍵字標註
// 給 Int 定義擴展
infix fun Int.shl(x: Int): Int {
……
}閉包

// 用中綴表示法調用擴展函數ide

1 shl 2函數式編程

// 等同於這樣函數

1.shl(2)
參數
函數參數使用 Pascal 表示法定義,即 name: type。參數用逗號隔開。每一個參數必須有顯式類型。優化

fun powerOf(number: Int, exponent: Int) {
……
}
默認參數
函數參數能夠有默認值,當省略相應的參數時使用默認值。與其餘語言相比,這能夠減小 重載數量。ui

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size()) {
……
}
默認值經過類型後面的 = 及給出的值來定義。

覆蓋方法老是使用與基類型方法相同的默認參數值。 當覆蓋一個帶有默認參數值的方法時,必須從簽名中省略默認參數值:

open class A {
open fun foo(i: Int = 10) { …… }
}

class B : A() {
override fun foo(i: Int) { …… } // 不能有默認值
}
命名參數
能夠在調用函數時使用命名的函數參數。當一個函數有大量的參數或默認參數時這會很是方便。


給定如下函數

fun reformat(str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: 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 是一種只有一個值——Unit 的類型。這個 值不須要顯式返回

fun printHello(name: String?): Unit {
if (name != null)
println("Hello ${name}")
else
println("Hi there!")
// `return Unit` 或者 `return` 是可選的
}
Unit 返回類型聲明也是可選的。上面的代碼等同於

fun printHello(name: String?) {
……
}
單表達式函數
當函數返回單個表達式時,能夠省略花括號而且在 = 符號以後指定代碼體便可

fun double(x: Int): Int = x * 2
當返回值類型可由編譯器推斷時,顯式聲明返回類型是可選的

fun double(x: Int) = x * 2
顯式返回類型
具備塊代碼體的函數必須始終顯式指定返回類型,除非他們旨在返回 Unit,在這種狀況下它是可選的。 Kotlin 不推斷具備塊代碼體的函數的返回類型,由於這樣的函數在代碼體中可能有複雜的控制流,而且返回 類型對於讀者(有時甚至對於編譯器)是不明顯的。

可變數量的參數(Varargs)
函數的參數(一般是最後一個)能夠用 vararg 修飾符標記:

fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // ts is an Array
result.add(t)
return result
}
容許將可變數量的參數傳遞給函數:

val list = asList(1, 2, 3)
在函數內部,類型 T 的 vararg 參數的可見方式是做爲 T 數組,即上例中的 ts 變量具備類型 Array <out T>。

只有一個參數能夠標註爲 vararg。若是 vararg 參數不是列表中的最後一個參數, 可使用 命名參數語法傳遞其後的參數的值,或者,若是參數具備函數類型,則經過在括號外部 傳一個 lambda。

當咱們調用 vararg-函數時,咱們能夠一個接一個地傳參,例如 asList(1, 2, 3),或者,若是咱們已經有一個數組 並但願將其內容傳給該函數,咱們使用伸展(spread)操做符(在數組前面加 *):

val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)
函數做用域
在 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())
}

局部函數能夠訪問外部函數(即閉包)的局部變量,因此在上例中,visited 能夠是局部變量。

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])
}
成員函數
成員函數是在類或對象內部定義的函數

class Sample() {
fun foo() { print("Foo") }
}
成員函數以點表示法調用

Sample().foo() // 建立類 Sample 實例並調用 foo
關於類和覆蓋成員的更多信息參見類和繼承

泛型函數
函數能夠有泛型參數,經過在函數名前使用尖括號指定。


fun <T> singletonList(item: T): List<T> {
// ……
}
關於泛型函數的更多信息參見泛型

內聯函數
內聯函數在這裏講述

擴展函數
擴展函數在其自有章節講述

高階函數和 Lambda 表達式
高階函數和 Lambda 表達式在其自有章節講述

尾遞歸函數
Kotlin 支持一種稱爲尾遞歸的函數式編程風格。 這容許一些一般用循環寫的算法改用遞歸函數來寫,而無堆棧溢出的風險。 當一個函數用 tailrec 修飾符標記並知足所需的形式時,編譯器會優化該遞歸,留下一個快速而高效的基於循環的版本。

tailrec fun findFixPoint(x: Double = 1.0): Double
= if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))

這段代碼計算餘弦的不動點(fixpoint of cosine),這是一個數學常數。 它只是重複地從 1.0 開始調用 Math.cos,直到結果再也不改變,產生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/finally 塊中。目前尾部遞歸只在 JVM 後端中支持。

相關文章
相關標籤/搜索