Kotlin擴(kuò)展方法超詳細(xì)介紹
前言
在這一節(jié)為大家繼續(xù)帶來(lái) Kotlin 中的一些高級(jí)的內(nèi)容:Kotlin 中的 Kotlin 擴(kuò) 展(Extensions)。
Kotlin 能夠擴(kuò)展一個(gè)類的新功能而無(wú)需繼承該類。 例如,你可以為一個(gè)你不 能修改的來(lái)自第三方庫(kù)中的類編寫(xiě)一個(gè)新的函數(shù)。 這個(gè)新增的函數(shù)就像那個(gè) 原始類本來(lái)就有的函數(shù)一樣,可以用普通的方法調(diào)用。 這種機(jī)制稱為 擴(kuò)展 函數(shù) 。此外,也有 擴(kuò)展屬性 , 允許你為一個(gè)已經(jīng)存在的類添加新的屬 性。想想是不是感覺(jué)很瘋狂呢?那接下來(lái)就往我們開(kāi)啟這種瘋狂吧。
一、擴(kuò)展方法
Kotlin 的擴(kuò)展函數(shù)可以讓你作為一個(gè)類成員進(jìn)行調(diào)用的函數(shù),但是是定義在這 個(gè)類的外部。這樣可以很方便的擴(kuò)展一個(gè)已經(jīng)存在的類,為它添加額外的方 法。在 Kotlin 源碼中,有大量的擴(kuò)展函數(shù)來(lái)擴(kuò)展 Java,這樣使得 Kotlin 比 Java 更方便使用,效率更高。
1.擴(kuò)展方法的原型

2.擴(kuò)展方法的使用
在 Kotlin 中使用
class Jump {
fun test() {
println("jump test")
//在被擴(kuò)展的類中使用
doubleJump(1f)
}
}
fun Jump.doubleJump(howLong: Float): Boolean {
println("jump:$howLong")
println("jump:$howLong")
return true
}
Jump().doubleJump(2f)
//在被擴(kuò)展類的外部使用
Jump().test()在 Java 中使用
在 Java 中調(diào)用 Kotlin 擴(kuò)展,需要通過(guò)擴(kuò)展所在的文件名+.的方式進(jìn)行調(diào)用:
KotlinExtensionKt.doubleJump(new Jump(), 2.0f);
另外,需要注意的是我們需要為這個(gè)方法傳遞它被擴(kuò)展類的對(duì)象來(lái)作為接受者,為什么要傳遞接 受者對(duì)象,這是由擴(kuò)展的實(shí)現(xiàn)原理所決定的,在原理解析部分會(huì)講解。
二、Kotlin擴(kuò)展方法實(shí)現(xiàn)原理
在體驗(yàn)到 Kotlin 擴(kuò)展帶個(gè)我們高效編程的同時(shí),我們不禁要問(wèn)自己幾個(gè)問(wèn) 題:
Kotlin 的擴(kuò)展是怎么實(shí)現(xiàn)的?
Kotlin 的擴(kuò)展會(huì)不是有性能問(wèn)題?
接下來(lái)我們就從 Kotlin 反編譯出 Java 代碼上來(lái)一探究竟:
fun main() {
val test = mutableListOf(1, 2, 3)
test.swap(1, 2)
println(test)
}
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}反編譯出 Java 源碼
public final class KotlinExtensionKt {
public static final void main() {
List test = CollectionsKt.mutableListOf(new Integer[]{1, 2, 3});
swap(test, 1, 2);
boolean var1 = false;
System.out.println(test);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
public static final void swap(@NotNull List $this$swap, int index1,
int index2) {
Intrinsics.checkParameterIsNotNull($this$swap, "$this$swap");
int tmp = ((Number)$this$swap.get(index1)).intValue();
$this$swap.set(index1, $this$swap.get(index2));
$this$swap.set(index2, tmp);
}
}從反編譯出的 Java 源碼分析,擴(kuò)展函數(shù)的實(shí)現(xiàn)非常簡(jiǎn)單,它沒(méi)有修改接受者類型的成員, 僅僅 是通過(guò)靜態(tài)方法來(lái)實(shí)現(xiàn)的。所以我們不必?fù)?dān)心擴(kuò)展函數(shù)會(huì)帶來(lái)額外的性能消耗。
三、泛型擴(kuò)展方法
為了考慮到擴(kuò)展函數(shù)的通用型,我們可以借助上面課程中學(xué)習(xí)到的泛型,來(lái) 為擴(kuò)展方法進(jìn)行泛型化改造,以 fun MutableList.swap(index1: Int, index2: Int)為例,接下來(lái)我們?yōu)樗M(jìn)行泛型化改造:
//泛型化擴(kuò)展函數(shù)
fun <T> MutableList<T>.swap1(index1: Int, index2: Int) {
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
val test2 = mutableListOf("Android Q", "Android N", "Android M")
test2.swap1(0,1)
println(test2)四、擴(kuò)展屬性
擴(kuò)展屬性提供了一種方法能通過(guò)屬性語(yǔ)法進(jìn)行訪問(wèn)的 API 來(lái)擴(kuò)展。盡管它們 被叫做屬性,但是它們不能擁有任何狀態(tài),它不能添加額外的字段到現(xiàn)有的 Java 對(duì)象實(shí)例。
//為 String 添加一個(gè) lastChar 屬性,用于獲取字符串的最后一個(gè)字符
val String.lastChar: Char get() = this.get(this.length - 1)
///為 List 添加一個(gè) last 屬性用于獲取列表的最后一個(gè)元素,this 可以省略
val <T>List<T>.last: T get() = get(size - 1)
val listString = listOf("Android Q", "Android N", "Android M")
println("listString.last${listString.last}")五、為伴生對(duì)象添加擴(kuò)展
如果一個(gè)類定義了伴生對(duì)象 ,那么我們也可以為伴生對(duì)象定義擴(kuò)展函數(shù)與屬性:
class Jump {
companion object {}
}
fun Jump.Companion.print(str: String) {
println(str)
}
Jump.print("伴生對(duì)象的擴(kuò)展")就像伴生對(duì)象的常規(guī)成員一樣:可以只使用類名作為限定符來(lái)調(diào)用伴生對(duì)象 的擴(kuò)展成員;
六、Kotlin 中常用的擴(kuò)展
在 Kotlin 的源碼中定義了大量的擴(kuò)展,比如:let,run,apply,了解并運(yùn)用這些 函數(shù)能幫我們提高編碼效率,接下來(lái)就往我們一起揭開(kāi)這些擴(kuò)展函數(shù)的神秘面紗吧!
let 擴(kuò)展
函數(shù)原型:
fun <T, R> T.let(f: (T) -> R): R = f(this)
let 擴(kuò)展函數(shù)的實(shí)際上是一個(gè)作用域函數(shù),當(dāng)你需要去定義一個(gè)變量在一個(gè)特定的作用域范圍內(nèi),那么let 函數(shù)是一個(gè)不錯(cuò)的選擇;let 函數(shù)另一個(gè)作用就 是可以避免寫(xiě)一些判斷 null 的操作。
fun testLet(str: String?) {
//限制 str2 的作用域
str.let {
val str2 = "let 擴(kuò)展"
println(it + str2)
}
// println(str2)//報(bào)錯(cuò)
//避免為 null 的操作
str?.let {
println(it.length)
}
}run 擴(kuò)展
函數(shù)原型:
fun <T, R> T.run(f: T.() -> R): R = f()
run 函數(shù)只接收一個(gè) lambda 函數(shù)為參數(shù),以閉包形式返回,返回值為最后一 行的值或者指定的 return 的表達(dá)式,在 run 函數(shù)中可以直接訪問(wèn)實(shí)例的公有屬性和方法。
data class Room(val address: String, val price: Float, val size: Float)
fun testRun(room: Room) {
room.run {
println("Room:$address,$price,$size")
}
}apply 擴(kuò)展
函數(shù)原型:
fun <T> T.apply(f: T.() -> Unit): T { f(); return this }apply 函數(shù)的作用是:調(diào)用某對(duì)象的 apply 函數(shù),在函數(shù)范圍內(nèi),可以任意調(diào) 用該對(duì)象的任意方法,并返回該對(duì)象。
從結(jié)構(gòu)上來(lái)看 apply 函數(shù)和 run 函數(shù)很像,唯一不同點(diǎn)就是它們各自返回的 值不一樣,run 函數(shù)是以閉包形式返回最后一行代碼的值,而 apply 函數(shù)的返 回的是傳入對(duì)象的本身。
apply 一般用于一個(gè)對(duì)象實(shí)例初始化的時(shí)候,需要對(duì)對(duì)象中的屬性進(jìn)行賦值。 或者動(dòng)態(tài) inflate 出一個(gè) XML 的 View 的時(shí)候需要給 View 綁定數(shù)據(jù)也會(huì)用 到,這種情景非常常見(jiàn)。
fun testApply() {
ArrayList<String>().apply {
add("testApply")
add("testApply")
add("testApply")
println("$this")
}.let { println(it) }
}七、案例
使用 Kotlin 擴(kuò)展為控件綁定監(jiān)聽(tīng)器減少模板代碼
定義擴(kuò)展
//為 Activity 添加 find 擴(kuò)展方法,用于通過(guò)資源 id 獲取控件
fun <T : View> Activity.find(@IdRes id: Int): T {
return findViewById(id)
}
//為 Int 添加 onClick 擴(kuò)展方法,用于為資源 id 對(duì)應(yīng)的控件添加 onClick 監(jiān)聽(tīng)
fun Int.onClick(activity: Activity, click: () -> Unit) {
activity.find<View>(this).apply {
setOnClickListener {
click()
}
}
}應(yīng)用擴(kuò)展
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView = find<TextView>(R.id.test)
R.id.test.onClick(this) {
textView.text = "Kotlin 泛型"
}
}
}在這個(gè)案例中我們通過(guò)兩個(gè)擴(kuò)展方法,大大減少了我們?cè)讷@取控件,以及為 控件綁定 onClick 監(jiān)聽(tīng)時(shí)候的模板代碼,而且代碼可讀性更高,更加直觀, 這便是 Kotlin 擴(kuò)展的強(qiáng)大之處。
Kotlin 擴(kuò)展的應(yīng)用案例遠(yuǎn)不止這些,需要大家在下去之后能夠活學(xué)活用,來(lái)發(fā) 掘?qū)儆谀阕约旱?Kotlin 擴(kuò)展吧。
到此這篇關(guān)于Kotlin擴(kuò)展方法超詳細(xì)介紹的文章就介紹到這了,更多相關(guān)Kotlin擴(kuò)展內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android自定義View實(shí)現(xiàn)字母導(dǎo)航欄
通常手機(jī)通訊錄都會(huì)有索引欄,這篇文章主要介紹了Android自定義View實(shí)現(xiàn)字母導(dǎo)航欄,現(xiàn)在分享給大家。2016-10-10
Android基于高德地圖完全自定義Marker的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Android基于高德地圖完全自定義Marker的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07
Android 單例模式實(shí)現(xiàn)可復(fù)用數(shù)據(jù)存儲(chǔ)的詳細(xì)過(guò)程
本文介紹了如何使用單例模式實(shí)現(xiàn)一個(gè)可復(fù)用的數(shù)據(jù)存儲(chǔ)類,該類可以存儲(chǔ)不同類型的數(shù)據(jù),并提供統(tǒng)一的接口來(lái)訪問(wèn)這些數(shù)據(jù),通過(guò)雙重檢查鎖定機(jī)制,該類在多線程環(huán)境下是線程安全的,感興趣的朋友跟隨小編一起看看吧2025-02-02
Android使用RecycleView實(shí)現(xiàn)拖拽交換item位置
這篇文章主要為大家詳細(xì)介紹了Android使用RecycleView實(shí)現(xiàn)拖拽交換item位置,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
android開(kāi)發(fā)中獲取手機(jī)分辨率大小的方法
不管是在我們的布局還是在實(shí)現(xiàn)代碼中進(jìn)行操控,我們的靈活性都不是局限于一個(gè)固定的數(shù)值,而是面對(duì)不同的手機(jī)對(duì)象都有一個(gè)適應(yīng)的數(shù)值。2013-04-04
Android DigitalClock組件用法實(shí)例
這篇文章主要介紹了Android DigitalClock組件用法,結(jié)合實(shí)例形式分析了DigitalClock組件的布局調(diào)用技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2016-01-01
Android點(diǎn)亮屏幕或屏幕解鎖和鎖定以及其他相關(guān)權(quán)限實(shí)現(xiàn)代碼
本文將帶你實(shí)現(xiàn)Android屏幕解鎖和鎖定;Android屏幕常亮/點(diǎn)亮以及其他相關(guān)權(quán)限,感興趣的朋友可以參考下,希望本文對(duì)你有所幫助2013-01-01
Android?完整購(gòu)物商城界面的實(shí)現(xiàn)案例
這篇文章為大家?guī)?lái)一個(gè)Android?完整購(gòu)物商城的界面具體的實(shí)現(xiàn),案例中含有商品列表的顯示,為商城最重要的功能之一,感興趣的朋友來(lái)看看吧2022-03-03

