網站首頁 編程語言 正文
正文
Kotlin中函數都是頭等的,這意味著它可以存儲在變量與數據結構中、作為參數傳遞給其他高階函數以及從其他高階函數返回。可以向操作任何其他非函數值一樣操作函數。
為促成這點,作為一門靜態類型編程語言的Kotlin使用一系列函數類型來表示函數并提供了一組專門的語言結構,例如Lambda表達式。
這段話來自Kotlin文檔。從沒有接觸過Kotlin的話這段話的意思很難理解,上面有三個關鍵詞【高階函數】、【函數類型】、【Lambda表達式】先分析這三個關鍵詞然后再對上面這段話進行理解。
1.函數類型
函數類型就是函數的類型, 變量有類型Int
、String
等,那函數的類型到底是指什么?
fun lastElement(str: String): Char { return str[str.length - 1] }
上面的代碼是之前用過的,意思是獲取字符串的最后一個字符,其中參數類型是String
,返回值類型是Char
,將其抽出來就是【String -> Char
】這就代表了函數的類型,一句話概括就是:將函數的【參數類型】和【返回值類型】抽象出來就得到了函數的【函數類型】。【(String) -> Char
】的意思就是參數類型是【String
】,返回值類型是【Char
】的函數類型。這個比較好理解。類似的還有Kotlin中繼承自BaseAdapter
的幾個方法
//函數類型:() -> Int override fun getCount(): Int { } //函數類型:(Int) -> Any override fun getItem(position: Int): Any { } //函數類型:(Int) -> Long override fun getItemId(position: Int): Long { }
2.高階函數
理解餓了函數類型再來看下高階函數。
高階函數是將函數用作參數或返回值的函數, 這是高階函數的定義。
- 函數用作參數的高階函數寫法
fun main() { val result = answer(20) { 10 } println("result:$result") //輸出結果:result:30 } /** * 高階函數 * 函數類型:(Int, add方法) -> Int */ fun answer(num: Int, add: () -> Int): Int { return num + add() }
上面的代碼用的是高階函數中的函數用作參數的寫法,定義一個answer
方法,添加一個參數num和函數add,因為add
方法返回值是一個Int類型因此可以跟num直接相加并返回結果,代碼沒有實際意義就是個例子。
這里可能會產生一個疑問:為什么result
的調用方式是成立的?
將上面的代碼轉換成Java的寫法就清楚了
public final class HighFunctionKt { public static final void main() { int result = answer(20, (Function0)null.INSTANCE); String var1 = "result:" + result; boolean var2 = false; System.out.println(var1); } // $FF: synthetic method public static void main(String[] var0) { main(); } public static final int answer(int num, @NotNull Function0 add) { Intrinsics.checkNotNullParameter(add, "add"); return num + ((Number)add.invoke()).intValue(); } }
可以看到add
方法被轉換成了Function0
,并且num與add方法的返回值進行了相加,那么這里有一個新的疑問invoke
是什么?invoke
是Function0
的一個方法,作用就是調用函數,在Functions.kt
類中,Function
的數量達到Function21
,從Function0
到Function21
的區別就是傳參數量的不同。
public interface Function0<out R> : Function<R> { /** Invokes the function. */ public operator fun invoke(): R }
再來看一個稍微復雜的高階函數例子,answer
函數中又添加了一個參數,同時函數參數也支持傳參
fun main() { val add = answer(10, 20) { num1, num2 -> num1 + num2 } val minus = answer(10, 20) { num1, num2 -> num1 - num2 } println("add:$add") //輸出結果:result:30 println("minus:$minus") //輸出結果:result:30 } fun answer(num1: Int, num2: Int, result: (Int, Int) -> Int): Int { return result(num1, num2) }
answer
方法做了改動,傳了兩個參數,函數類型的參數也傳入了兩個參數,這樣定義的作用是更靈活
- 函數用作返回值的高階函數寫法
fun main() { println(answer(10).invoke()) //輸出結果:輸入的數:10 } fun answer(num: Int): () -> String { return { "輸入的數:${num}" } }
這里的invoke
就是一個調用函數的功能。編譯成Java代碼如下所示:
public final class HighFunctionKt { public static final void main() { Object var0 = answer(10).invoke(); boolean var1 = false; System.out.println(var0); } // $FF: synthetic method public static void main(String[] var0) { main(); } @NotNull public static final Function0 answer(final int num) { return (Function0)(new Function0() { // $FF: synthetic method // $FF: bridge method public Object invoke() { return this.invoke(); } @NotNull public final String invoke() { return "輸入的數:" + num; } }); } }
- 應用場景舉例
Android開發過程中RecycleView
是常用的一個組件,但是它本身不支持點擊事件,現在,假設我們在Adapter中有兩個點擊事件,添加和刪除,常用的寫法會先定義一個接口,對該接口定義一個變量,然后定義一個方法,代碼如下:
private lateinit var mOnItemAddClickListener: OnItemAddClickListener private lateinit var mOnItemDeleteClickListener: OnItemDeleteClickListener interface OnItemAddClickListener { fun onItemAddClick(position: Int) } interface OnItemDeleteClickListener { fun onItemDeleteClick(position: Int) } fun setOnItemAddClickListener(onItemAddClickListener: OnItemAddClickListener) { mOnItemAddClickListener = onItemAddClickListener } fun setOnItemDeleteClickListener(onItemDeleteClickListener: OnItemDeleteClickListener) { mOnItemDeleteClickListener = onItemDeleteClickListener } holder.ivAdd.setOnClickListener { mOnItemAddClickListener.onItemAddClick(position) } holder.ivDelete.setOnClickListener { mOnItemDeleteClickListener.onItemDeleteClick(position) } adapter.setOnItemAddClickListener(object :DemoAdapter.OnItemAddClickListener{ override fun onItemAddClick(position: Int) { TODO("Not yet implemented") } }) adapter.setOnItemDeleteClickListener(object :DemoAdapter.OnItemDeleteClickListener{ override fun onItemDeleteClick(position: Int) { TODO("Not yet implemented") } })
用高階函數對其進行優化后的代碼如下:
private lateinit var mOnItemAddClickListener: (Int) -> Unit private lateinit var mOnItemDeleteClickListener: (Int) -> Unit fun setOnItemAddClickListener(listener: (Int) -> Unit) { mOnItemAddClickListener = listener } fun setOnItemDeleteClickListener(listener: (Int) -> Unit) { mOnItemDeleteClickListener = listener } holder.ivAdd.setOnClickListener { mOnItemAddClickListener.invoke(position) } holder.ivDelete.setOnClickListener { mOnItemDeleteClickListener.invoke(position) } adapter.setOnItemAddClickListener { } adapter.setOnItemDeleteClickListener { }
這兩種寫法的代碼進行對比可以發現高階函數的實現方式中沒有定義接口,同時它代碼量顯著減少,代碼也變得更加簡潔。
3.系統標準高階函數
系統的標準高階函數來自Standard.kt,里面的方法也是比較常用的
- run
public inline fun <R> run(block: () -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() }
block
是函數式參數的名稱,傳入的函數類型是()-> R
,R
是泛型
這段代碼的意思就是調用傳入的函數并返回結果,return block()
就是傳入函數的調用。
怎么用?or有什么用?
fun main() { run { println(add(1, 2)) } } fun add(num1: Int, num2: Int): Int { return num1 + num2 }
作用就是構建Lambda更方便
- T.run
public inline fun <T, R> T.run(block: T.() -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() }
這個run
更上面那個的區別在于它有一個接收者,有了這個接收者就可以使用上下文了,例如:
val num = 10 num.run { println("result:${this + 1}") } //輸出結果:result:11
還有一種情況,例如要從某個對象中取出它的一些屬性的值也可以通過T.run
,同時由于可以將this
省略因此代碼就可以這么寫:
class Person(val name:String, var age:Int) val person = Person("張三", 19) person.run{ println("name:$name") println("age:$age") }
再舉個例子,TextView利用T.run
修改屬性并賦值
holder.tvText.run { text = "自定義文本" setTextColor(context.getColor(R.color.color_000000)) textSize = 20F }
- whith
public inline fun <T, R> with(receiver: T, block: T.() -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return receiver.block() }
with
的使用方式與T.run
類似,with
在返回值上帶了一個接收者,看下面代碼
with(holder.tvText) { text = "自定義文本" setTextColor(context.getColor(R.color.color_000000)) textSize = 20F }
- T.apply
public inline fun <T> T.apply(block: T.() -> Unit): T { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } block() return this }
這個函數的意思就是接收者傳入了什么返回值就是什么,使用案例:
val person = Person("張三", 19) person.apply { println("name:$name") //輸出結果:張三 println("age:$age") //輸出結果:19 }.age = 10 //把張三的年齡修改我10歲 println("name:${person.name}") //輸出結果:張三 println("age:${person.age}") //輸出結果:10
有什么用?
Android中會遇到根據狀態修改Button樣式然后還要響應點擊事件的情況,常用寫法就不在這里講了,這里用T.apply
實現:
button.apply { text = "提交" setTextColor(context.getColor(R.color.color_000000)) background = context.getDrawable(R.drawable.shape_solid_4dp_4e6cf5) }.setOnClickListener { //點擊事件 }
這行代碼是不是很簡潔。
還有webview的使用
webView.apply { settings.javaScriptEnabled = true settings.useWideViewPort = true }.loadUrl("https://www.baidu.com/")
T.apply
很靈活,具體問題具體分析就好。
- T.also
public inline fun <T> T.also(block: (T) -> Unit): T { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } block(this) return this }
T.also
的意思就是用傳參調用指定函數并返回這個參數
button.also { it.text }
這里的button在調用also
之后在它的里面只能用it
也必須用it
,這個it
指的是button本身,而T.apply
中是this
指的是Button本身,并且這個this
是可以被省略的。使用過程中的區別不大。
- T.let
public inline fun <T, R> T.let(block: (T) -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block(this) }
T.let
的意思就是使用傳參調用指定函數然后返回結果,代碼如下
val person = Person("張三", 19) val str = "Hello Kotlin" val result = person.let { it.name == "張三" } println("result:$result") //輸出結果:result:true println("length:${str.let { it.length }}") //輸出結果:length:12
在Person類中有一個name = 張三的實例,經過let
判斷后返回true;獲取str字符串的長度得到最終結果12。
- T.takeIf
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? { contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } return if (predicate(this)) this else null }
T.takeIf
的意思就是如果符合條件則返回傳入的值,否則返回null
val person = Person("張三", 19) val result = person.takeIf { it.name == "李四" } println("result:${result?.name}") //輸出結果:result:null val result = person.takeIf { it.name == "張三" } //條件成立返回person對象
- T.takeUnless
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? { contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } return if (!predicate(this)) this else null }
T.takeUnless
和T.takeIf
正好相反,如果滿足條件則返回null,不滿足則返回正確的謂詞
val person = Person("張三", 19) val result = person.takeUnless { it.name == "李四" } println("result:${result?.name}") //輸出結果:result:張三 val result = person.takeUnless { it.name == "張三" } println("result:${result?.name}") //輸出結果:result:null
- repeat
public inline fun repeat(times: Int, action: (Int) -> Unit) { contract { callsInPlace(action) } for (index in 0 until times) { action(index) } }
action
函數就是一個重復執行的函數,從0開始
repeat(2) { println("執行第:${it}次") } //輸出結果: //執行第:0次 //執行第:1次
4.Lambda表達式
Lambda表達式在Java中已經用的比較多了,通過它可以簡化代碼和提高開發效率,所以我們可以把Lambda表達式理解為函數的簡寫。
例如view的點擊事件在Kotlin中調用時就是這樣的:
fun setOnClickListener(l: ((View!) -> Unit)?){ } //調用時可以這么寫: button.setOnClickListener{ }
開頭提出了一個結論:在Kotlin中函數是頭等的,為什么這么說呢?
- Kotlin的函數可以獨立于類之外,這就是頂層函數
- Kotlin的函數可以作為參數也可以作為函數,它被稱為高階函數和Lambda
- Kotlin的函數可以向變量一樣,這叫做函數引用
原文鏈接:https://juejin.cn/post/7170121913572802591
相關推薦
- 2022-12-25 終于明白tf.reduce_sum()函數和tf.reduce_mean()函數用法_python
- 2022-08-05 Entity?Framework主從表數據加載方式_C#教程
- 2022-08-15 解決多個el-checkbox內容折行對不齊問題(可以限制每行展示的個數)
- 2022-09-26 在?React?Native?中使用?CSS?Modules的配置方法_React
- 2022-05-23 Jenkins實現集群化管理以及流水線項目配置_nginx
- 2022-07-17 SQL?Server中鎖的用法_MsSql
- 2022-11-05 Rust使用libloader調用動態鏈接庫_Rust語言
- 2022-02-17 使用Postman測試接口提示Error: connect ECONNREFUSED 127.0.0
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支