目錄
- 高階函式
- 雙冒號
- 函式參考
- 類參考
- 屬性參考
- 匿名函式
- Lambda 運算式
- 例子
- 作用域函式
高階函式
高階函式是將函式用作引數或回傳值的函式,還可以把函式賦值給一個變數,
所有函式型別都有一個圓括號括起來的引數型別串列以及一個回傳型別:(A, B) -> C 表示接受型別分別為 A 與 B 兩個引數并回傳一個 C 型別值的函式型別, 引數型別串列可以為空,如 () -> A,Unit 回傳型別不可省略,
(Int) -> String
函式型別表示法可以選擇性地包含函式的引數名:(x: Int, y: Int) -> Point, 這些名稱可用于表明引數的含義,
(Button, ClickEvent) -> Unit
如需將函式型別指定為可空,請使用圓括號:((Int, Int) -> Int)?
fun a(funParam: (Int) -> String): String {
return funParam(1)
}
fun b(param: Int): String {
return param.toString()
}
呼叫
a(::b)
var d = ::b
b(1) // 呼叫函式
d(1) // 實際上會呼叫 d.invoke(1)
(::b)(1) // 用物件 :: b 后面加上括號來實作 b() 的等價操作, 實際上會呼叫 (::b).invoke(1)
b.invoke(1) // 報錯
物件是不能加個括號來呼叫的,但是函式型別的物件可以,為什么?因為這其實是個假的呼叫,它是 Kotlin 的語法糖,實際上你對一個函式型別的物件加括號、加引數,它真正呼叫的是這個物件的 invoke() 函式
雙冒號
:: 創建一個函式參考或者一個類參考
函式參考
fun isOdd(x: Int) = x % 2 != 0
我們可以很容易地直接呼叫它(isOdd(5)),但是我們也可以將其作為一個函式型別的值,例如將其傳給另一個函式,為此,我們使用 :: 運算子:
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd))
這里 ::isOdd 是函式型別 (Int) -> Boolean 的一個值,
如果我們需要使用類的成員函式或擴展函式,它需要是限定的,例如 String::toCharArray,
val args: Array<String> = arrayOf("1", "2")
args.filter(String::isNotEmpty)
class PdfPrinter {
fun println(any: Any) {
kotlin.io.println(any) //重名了可以用包名呼叫
}
}
val pdfPrinter = PdfPrinter()
args.forEach(pdfPrinter::println)
類參考
val c = MyClass::class
該參考是 KClass 型別的值
請注意,Kotlin 類參考與 Java 類參考不同,要獲得 Java 類參考, 請在 KClass 實體上使用 .java 屬性,
平時寫的類,其資訊都可以在這個KClass來獲取
屬性參考
data class MediaItem(val title: String, val url: String)
var items= mutableListOf<MediaItem>()
items
.sortedBy { it.title }
.map { it.url }
.forEach { print(it) }
items
.sortedBy(MediaItem::title)
.map(MediaItem::url)
.forEach(::println)
匿名函式
沒有名字的函式
要傳一個函式型別的引數,或者把一個函式型別的物件賦值給變數,除了用雙冒號來拿現成的函式使用,你還可以直接把這個函式挪過來寫:
fun b(param: Int): String {
return param.toString()
}
a(fun b(param: Int): String {
return param.toString()
});
val d = fun b(param: Int): String {
return param.toString()
}
//名字沒意義,省略
a(fun(param: Int): String {
return param.toString()
});
val d = fun(param: Int): String {
return param.toString()
}
如果你在 Java 里設計一個回呼的時候是這么設計的:
public interface OnClickListener {
void onClick(View v);
}
public void setOnClickListener(OnClickListener listener) {
this.listener = listener;
}
使用的時候是這么用的:
view.setOnClickListener(new OnClickListener() {
@Override
void onClick(View v) {
switchToNextPage();
}
});
kotlin寫法
fun setOnClickListener(onClick: (View) -> Unit) {
this.onClick = onClick
}
view.setOnClickListener(fun(v: View): Unit) {
switchToNextPage()
})
Lambda寫法:
view.setOnClickListener({ v: View ->
switchToNextPage()
})
Lambda 運算式
簡化匿名函式,代碼更簡潔
view.setOnClickListener({ v: View ->
switchToNextPage()
})
//如果 Lambda 是函式的最后一個引數,你可以把 Lambda 寫在括號的外面:
view.setOnClickListener() { v: View ->
switchToNextPage()
}
//而如果 Lambda 是函式唯一的引數,你還可以直接把括號去了:
view.setOnClickListener { v: View ->
switchToNextPage()
}
//另外,如果這個 Lambda 是單引數的,它的這個引數也省略掉不寫:
//根據背景關系推導,根據最后一行代碼來推斷出回傳值型別
view.setOnClickListener {
switchToNextPage()
}
Lambda 運算式的完整語法形式如下:
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
val sum = { x: Int, y: Int -> x + y }
多引數例子:
fold函式:將所提供的操作應用于集合元素并回傳累積的結果
val items = listOf(1, 2, 3, 4, 5)
// Lambdas 運算式是花括號括起來的代碼塊,
items.fold(0, {
// 如果一個 lambda 運算式有引數,前面是引數,后跟“->”
acc: Int, i: Int ->
print("acc = $acc, i = $i, ")
val result = acc + i
println("result = $result")
// lambda 運算式中的最后一個運算式是回傳值:
result
})
// lambda 運算式的引數型別是可選的,如果能夠推斷出來的話:
val joinedToString = items.fold("Elements:", { acc, i -> acc + " " + i })
輸出:
acc = 0, i = 1, result = 1
acc = 1, i = 2, result = 3
acc = 3, i = 3, result = 6
acc = 6, i = 4, result = 10
acc = 10, i = 5, result = 15
joinedToString = Elements: 1 2 3 4 5
總結:
函式不能直接傳遞或者賦給某個變數,需要函式型別實體化,有三種方式:
使用已有宣告的可呼叫參考
1.函式參考
使用函式字面值的代碼塊
2.匿名函式
3.lambda 運算式
例子
實作介面
var onVideoStartCallBack: (() -> Unit)? = null
onVideoStartCallBack?.invoke()
videioView.onVideoStartCallBack = {
}
函式里實作介面
object UploaderListHelper {
fun startTaskUpload(activity: Activity, startCallBack: ((Int) -> Unit)?) {
startCallBack.invoke(position)
}
}
UploaderListHelper.startTaskUpload(activity) {
refreshProgress(it)
}
作用域函式
Kotlin 標準庫包含幾個函式,它們的唯一目的是在物件的背景關系中執行代碼塊,當對一個物件呼叫這樣的函式并提供一個 lambda 運算式時,它會形成一個臨時作用域,在此作用域中,可以訪問該物件而無需其名稱,這些函式稱為作用域函式,共有以下五種:let、run、with、apply 以及 also,
這些函式基本上做了同樣的事情:在一個物件上執行一個代碼塊,不同的是這個物件在塊中如何使用,以及整個運算式的結果是什么,
目的:簡潔
val person = findPerson();
//person是可null的,所以需要?
println(person?.age)
println(person?.name)
//上面太麻煩,findPerson加了?,所以后面不需要了,減少的判空操作,let可以安全呼叫
findPerson()?.let { person ->
person.work()
println(person.age)
}
//還可以更簡潔,person也不用寫
findPerson()?.apply {
work()
println(age)
}
使?時可以通過簡單的規則作出一些判斷
回傳自身
回傳值是它本身
從 apply 和 also 中選
作?域中使? this 作為引數選擇 apply
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
作?域中使? it 作為引數選擇 also
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
with 非擴展函式
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' is called with argument $this")
println("It contains $size elements")
}
不需要回傳自身
從 run 和 let 中選擇
作用域中使用 this 作為引數,選擇 run
作用域中使用 it 作為引數,選擇 let, 適合配合空判斷的時候
val service = MultiportService("https://example.kotlinlang.org", 80)
val result = service.run {
port = 8080
query(prepareRequest() + " to port $port")
}
// 同樣的代碼如果用 let() 函式來寫:
val letResult = service.let {
it.port = 8080
it.query(it.prepareRequest() + " to port ${it.port}")
}
it作為引數的好處
let 允許我們自定義引數名字,使可讀性更強,如果傾向可讀性可以選擇 T.let

參考文章
Kotlin 的高階函式、匿名函式和 Lambda 運算式
Kotlin官網
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/553413.html
標籤:其他
上一篇:詳細解讀Java中Map集合的底層原理(干貨+原始碼解讀)
下一篇:返回列表
