1. 前言
我們知道,在程序中,通常情況下。一個類會有自己的方法(函數)以及屬性,這些方法代表了該類的特性或者說具有的能力。今天我們一起來研究一下 kotlin中的函數。
2.如何聲明一個函數
在 kotlin 中,我們通過關鍵字 fun 來聲明一個函數
fun multiply(x:Int,y:Int):Int{
... //代碼塊
return x*y
}
如上面的代碼所示:我們定義了函數multiply()并指定其返回類型為 Int 類型。
3.Lambda表達式
3.1 Lambda表達式介紹
從Java8 開始,Lambda表達式在 Lambda表達式,在其他的編程語言中(例如:Scala
語言),這種表達式是語法糖中的一種,在 kotlin 中,也支持這種語法,它允許把函數作為一個方法的參數,可以使代碼變的更加簡潔緊湊。
Lambda
表達式的本質其實是匿名函數
,因為在其底層實現中還是通過匿名函數
來實現的。但是我們在用的時候不必關心起底層實現。不過Lambda
的出現確實是減少了代碼量的編寫,同時也是代碼變得更加簡潔明了。
3.2 Java 8 Lambda 表達式
3.2.1 語法:
(parameters) -> expression
或
(parameters) ->{ statements; }
3.2.2 特征
可選類型聲明:不需要聲明參數類型,編譯器可以統一識別參數值。
可選的參數圓括號:一個參數無需定義圓括號,但多個參數需要定義圓括號。
可選的大括號:如果主體包含了一個語句,就不需要使用大括號。
可選的返回關鍵字:如果主體只有一個表達式返回值則編譯器會自動返回值,大括號需要指定明表達式返回了一個數值。
3.2.3 代碼如下:
// 1. 不需要參數,返回值為 5
() -> 5
// 2. 接收一個參數(數字類型),返回其2倍的值
x -> 2 * x
// 3. 接受2個參數(數字),并返回他們的差值
(x, y) -> x – y
// 4. 接收2個int型整數,返回他們的和
(int x, int y) -> x + y
// 5. 接受一個 string 對象,并在控制臺打印,不返回任何值(看起來像是返回void)
(String s) -> System.out.print(s)
從上面可以看出,代碼簡潔了不少。
我們再來看一個例子,在java中假如要計算兩個數的和,則代碼如下:
import java.util.function.BiFunction;
public class helloword {
public static void main(String[] args) {
BiFunction<Integer,Integer,Integer> sum=(Integer x, Integer y) ->{
return x+y;
};
System.out.println("sum(3, 4) = " + sum.apply(3, 4));
}
}
運行結果如下:
但是,在 java 8 以下版本,并不支持 Lambda 表達式,而這個時候.kotlin 完美的兼容了 java 8 以下版本對 lamdba 表達式的支持,并且能夠進行混合開發。
3.3 kotlin Lamdba 表達式
好,我們再來看看 Android 中 kotlin 的使用,Kotlin語言中
3.3.1 Lamdab 表達式 的聲明:
lambda表達式的完整語法如下:
{ params -> expressions }
params表示參數列表,expressions表示具體實現,可以是單行語句,也可以是多行語句。
Lamdba 表達式的值為大括號最后一行的值。
3.3,2 Lamdba 表達式的類型表示:
() ->unit 無參,返回值為Unit
(Int)-> Int 傳入整型,返回一個整型
(String,(String)->String)->Boolean 傳入字符串,Lamdba 表達式,返回 Boolean
3.3.3 Lamdba 表達式的調用:
- 使用 () 進行調用,相當于invoke()
3.3.4 Lamdba 表達式的簡化:
args.forEach(){ println(it) } // 如果函數的最后一個參數是lamdba 表達式,則可以將lamdba 放在括號外面
args.forEach{ println(it) } // 如果函數參數只有一個lamdba表達式,則調用時小括號可以省略。
args.forEach(::print) //入參,返回值與形參一致的函數可以用函數引用的方式作為實參傳入。
上述同樣的功能,kotlin 語言的實現如下:
- 自定義函數來實現:
fun main(args: Array<String>) {
val result=sum(3,4)
println(result)
}
fun sum(arg1:Int,arg2:Int)=arg1+arg2
- 使用 Lamdba 表達式實現:
fun main(args: Array<String>) {
println(result(3, 4))
}
val result = { arg1: Int, arg2: Int ->
println("$arg1+$arg2=${arg1 + arg2}")
arg1 + arg2
}
我們再來看個例子,要求:遍歷kotlin 中main函數的參數,
- 示例代碼:Android 中最常見的點擊事件
tv_toLogin.setOnClickListener(object:View.OnClickListener{
override fun onClick(v: View?) {
Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show()
}
})
- 使用 Lambda 表達式的點擊事件
tv_toLogin.setOnClickListener({
Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show()
})
怎么樣?有沒有感覺 lamdba 表達式使用起來既簡潔又優雅。
4. 高階函數
4.1 什么是高階函數
高階函數就是以另一個函數作為參數或返回值的函數,Kotlin 可以以 lambda 或參數引用作為參數或返回值,所以,任何以 lambda 或函數引用作為參數或返回值的都是高階函數。
4.2 常見的高階函數
4.2.1 forEach()函數
fun main(args: Array<String>) {
val list= listOf(1,2,3,4,5,6,7,8)
val newList=ArrayList<Int>()
list.forEach{
val newElement=it *2+3
newList.add(newElement)
}
newList.forEach(::println)
}
運行結果如下:
4.2.2 :map()函數
map: 接受一個lambda表達式,并且有返回值,形成一個新的list,實現對集合中的元素進行修改
好,我們通過高階函數 map 來實現如上的效果:
fun main(args: Array<String>) {
val list= listOf(1,2,3,4,5,6,7,8)
val newList=list.map {
it*2+3
}
println(newList)
}
運行結果如下:
以上代碼中通過高階函數 map 實現了對集合中每個元素乘以2再加3的操作,不用去遍歷集合中每個元素,是不是簡單了許多。
4.2.3 flatMap()函數
flatMap是map和flat兩個函數的“復合邏輯,可以將集合中的數據進行合并成一個集合。
示例代碼如下:
fun main(args: Array<String>) {
val list= listOf(
1..20,
21..30,
31..100
)
val flatList=list.flatMap {
it.map {
"NO.$it"
}
}
flatList.forEach{
println(it)
}
}
運行結果如下: 將集合中數字從1~100打印輸出,每個數字前標有“NO.”
4.2.4 filter()函數
傳入Lambda 表達式為 true 是,保留該元素;使用filter對集合進行按條件過濾
fun main(args: Array<String>) {
val list= listOf(1,2,3,4,5,6,7,8,9)
val result=list.filter {
it%2==0
}
println(result)
}
運行結果如下:
4.3 kotlin 中的 特殊函數
4.3.1 run()函數
該函數實際上可以說是let和with兩個函數的結合體,run函數接收一個lambda函數為參數,以閉包形式返回,返回值為最后一行的值或者指定的return的表達式。
示例代碼如下:
fun main(args: Array<String>) {
FunKotlin().myFun()
run {
FunKotlin().myFun()
}
run {
println("kotlin")
}
}
class FunKotlin{
fun myFun():String{
println("開始執行myFun()函數")
return "kotlin 中的特殊函數"
}
}
運行結果如下:
在上面的代碼中,我們定義了myFun()函數并通過run() 進行調用,調用的結果即為myFun()的結果。
run() 源碼如下:
/**
* Calls the specified function [block] and returns its result.
*/
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
如代碼所示:我們傳入block()參數,最終返回了block() 的執行結果。
4.3.2 apply()函數
源碼如下:
/**
* Calls the specified function [block] with `this` value as its receiver and returns `this` value.
*/
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
如代碼所示:我們傳入 block() 函數,先是調用了block()函數,然后返回當前的調用者對象this,也就是說先執行完block()代碼塊邏輯后,再次返回當前的調用者對象。
示例代碼如下:
fun main(args: Array<String>) {
testApply()
}
fun testApply(){
val list= mutableListOf<String>()
list.add("A")
list.add("B")
list.add("C")
list.add("D")
list.add("E")
println("普通寫法:list=${list}")
val applyList=mutableListOf<String>().apply {
add("A")
add("B")
add("C")
add("D")
add("E")
println("使用apply 函數寫法 this=${this}")
}
}
運行結果如下:
如代碼所示:我們的需求是創建一個集合并向其中添加元素"A",“B”,"C",“D”,“E”,然后打印出該集合,相比普通寫法,使用apply() 函數顯然簡潔了許多。
4.3.3 let() 函數
let擴展函數的實際上是一個作用域函數,當你需要去定義一個變量在一個特定的作用域范圍內,let函數的是一個不錯的選擇;let函數另一個作用就是可以避免寫一些判斷null的操作
源碼如下:
/**
* Calls the specified function [block] with `this` value as its argument and returns its result.
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
如代碼所示,我們重點看最后一行return block(this),就是說把當前調用對象作為參數傳入block()代碼塊中。
示例代碼如下:我們以Android中在適配器 adapter 中進行網絡圖片的加載。
context?.let {
Glide.with(it).load(item.envelopePic).crossFade().into(helper.getView<ImageView>(R.id.iv_envelopePic))
}
在上面的代碼中,it 指代的即是 context,意為上下文。</pre>
4.3.4 also()函數
源碼如下:
/**
* Calls the specified function [block] with `this` value as its argument and returns `this` value.
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
看最后兩行代碼,先是執行了block(this),隨后返回this,即當前的調用者對象。
示例代碼如下:
fun testAlsoFun() {
val a = "ABC".also {
println(it) //輸出:ABC
}
println(a) //輸出:ABC
a.let {
println(it) //輸出:ABC
}
}
fun main(args: Array<String>) {
testAlsoFun()
}
在上面的代碼中,字符串“ABC”調用了also(),會打印出調用者 “ABC”.
4.3.5 with() 函數
源碼如下:
/**
* Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
我們看到with()函數傳入了一個接收者對象receiver,然后使用該對 象receiver去調用傳入的Lambda代碼塊receiver.block()。
- 示例代碼在 Android中我們初始化一個控件并給其賦值,Java 語言實現如下:
TextView text=(TextView)findViewById(R.id.tv_text)
text.setText("哈哈哈")
text.setTextSize(23)
- kotlin 語言實現如下:
with(tv_text){
text="哈哈哈"
textSize=23
}
在上面的代碼中,實現的功能是一樣的,但是顯然 kotlin 語言更加的簡潔。