麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 開發 > 綜合 > 正文

Kotlin擴展函數及實現機制的深入探索

2024-07-21 23:03:46
字體:
來源:轉載
供稿:網友

前言

2017年Google IO大會宣布使用Kotlin作為Android的官方開發語言,相比較與典型的面相對象的JAVA語言,Kotlin作為一種新式的函數式編程語言,也有人稱之為Android平臺的Swift語言。

先讓我們看下實現同樣的功能,Java和Kotiln的對比:

// JAVA,20多行代碼,充斥著findViewById,類型轉換,匿名內部類這樣的無意義代碼public class MainJavaActivity extends Activity { @Override public void onCreate(@Nullable Bundle savedInstanceState) {  super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView label = (TextView) findViewById(R.id.label); Button btn = (Button) findViewById(R.id.btn); label.setText("hello"); label.setOnClickListener(new View.OnClickListener() {    @Override  public void onClick(View v) {  Log.d("Glen","onClick TextView");  } }); btn.setOnClickListener(new View.OnClickListener(){    @Override  public void onClick(View v) {  Log.d("Glen","onClick Button");  } }); }}

再來看Kotlin

// Kotlin,沒有了冗余的findViewById,我們可以直接對資源id進行操作,也不需要匿名內部類的聲明,更關注函數的實現本身,拋棄了復雜的格式class MainKotlinActivity:Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) R.id.label.setText("hello") R.id.label.onClick { Log.d("Glen","onClick TextView") } R.id.btn.onClick { Log.d("Glen","onClick Button") } }}

實現這些需要借助Kotlin的擴展函數與高階函數,本文主要介紹一下擴展函數。

什么是擴展函數?

擴展函數數是指在一個類上增加一種新的行為,甚至我們沒有這個類代碼的訪問權限。這是一個在缺少有用函數的類上擴展的方法,Kotlin能夠為我們做到那些令人關注的事情,而這些Java做不到。

在Java中,通常會實現很多帶有static方法的工具類,而Kotlin中擴展函數的一個優勢是我們不需要在調用方法的時候把整個對象當作參數傳入,它表現得就像是屬于這個類的一樣,而且我們可以使用this關鍵字和調用所有public方法。

1. Kotlin 擴展函數與擴展屬性(Kotlin Extensions)

Kotlin 能夠擴展一個類的新功能而無需繼承該類,或者對任意的類使用像“裝飾者(Decorator)”這樣的設計模式。這些都是通過叫做“擴展(extensions)”的特殊聲明實現的。Kotlin擴展聲明既支持擴展函數也支持擴展屬性,本文主要討論擴展函數,至于擴展屬性實現的機制類似。

擴展函數的聲明非常簡單,他的關鍵字是.,此外我們需要一個“接受者類型(recievier type)”來作為他的前綴。以類MutableList<Int>為例,現在為它擴展一個swap方法,如下:

fun MutableList<Int>.swap(index1:Int,index2:Int) { val tmp = this[index1] this[index1] = this[index2] this[index2] = tmp}

MutableList<T>是kotlin提供的基礎庫collection中的List容器類,這里在聲明里作為“接受者類型”,.作為聲明關鍵字,swap是擴展函數名,其余和Kotlin聲明一個普通函數并無區別。

額外提一句,Kotlin的this語法要比JAVA更靈活,這里擴展函數體里的this代表的是接受者類型對象。

如果我們想要調用這個擴展函數,可以這樣:

fun use(){ val list = mutableListOf(1,2,3) list.swap(1,2)}

2. Kotlin擴展函數是怎么實現的

擴展函數的調用看起來就像是原生方法一樣自然,使用起來也非常順手,但是這樣的方法會不會帶來性能方面的掣肘呢?有必要探究一下Kotlin是如何實現擴展函數的,直接分析Kotlin源碼難度還是挺大,還好Android Studio提供了一些工具,我們可以通過Kotlin ByteCode指令,查看Kotlin語言轉換的字節碼文件,仍以MutableList<Int>,swap為例,轉換為字節碼之后的文件如下:

// ================com/example/glensun/demo/extension/MutableListDemoKt.class =================// class version 50.0 (50)// access flags 0x31public final class com/example/glensun/demo/extension/MutableListDemoKt {  // access flags 0x19 // signature (Ljava/util/List<Ljava/lang/Integer;>;II)V // declaration: void swap(java.util.List<java.lang.Integer>, int, int) public final static swap(Ljava/util/List;II)V @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 L0 ALOAD 0 LDC "$receiver" INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V L1 LINENUMBER 8 L1 ALOAD 0 ILOAD 1 INVOKEINTERFACE java/util/List.get (I)Ljava/lang/Object; CHECKCAST java/lang/Number INVOKEVIRTUAL java/lang/Number.intValue ()I ISTORE 3 L2 LINENUMBER 9 L2 ALOAD 0 ILOAD 1 ALOAD 0 ILOAD 2 INVOKEINTERFACE java/util/List.get (I)Ljava/lang/Object; INVOKEINTERFACE java/util/List.set (ILjava/lang/Object;)Ljava/lang/Object; POP L3 LINENUMBER 10 L3 ALOAD 0 ILOAD 2 ILOAD 3 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; INVOKEINTERFACE java/util/List.set (ILjava/lang/Object;)Ljava/lang/Object; POP L4 LINENUMBER 11 L4 RETURN L5 LOCALVARIABLE tmp I L2 L5 3 LOCALVARIABLE $receiver Ljava/util/List; L0 L5 0 LOCALVARIABLE index1 I L0 L5 1 LOCALVARIABLE index2 I L0 L5 2 MAXSTACK = 4 MAXLOCALS = 4 @Lkotlin/Metadata;(mv={1, 1, 7}, bv={1, 0, 2}, k=2, d1={"/u0000/u0012/n/u0000/n/u0002/u0010/u0002/n/u0002/u0010!/n/u0002/u0010/u0008/n/u0002/u0008/u0003/u001a /u0010/u0000/u001a/u00020/u0001*/u0008/u0012/u0004/u0012/u00020/u00030/u00022/u0006/u0010/u0004/u001a/u00020/u00032/u0006/u0010/u0005/u001a/u00020/u0003/u00a8/u0006/u0006"}, d2={"swap", "", "", "", "index1", "index2", "production sources for module app"}) // compiled from: MutableListDemo.kt}// ================META-INF/production sources for module app.kotlin_module =================

這里的字節碼已經相當直觀,更令人驚喜的是Android Studio還具備將字節碼轉為JAVA文件的能力,點擊上面的Decompile按鈕,可以得到如下JAVA代碼:

import java.util.List;import kotlin.Metadata;import kotlin.jvm.internal.Intrinsics;import org.jetbrains.annotations.NotNull;@Metadata( mv = {1, 1, 7}, bv = {1, 0, 2}, k = 2, d1 = {"/u0000/u0012/n/u0000/n/u0002/u0010/u0002/n/u0002/u0010!/n/u0002/u0010/b/n/u0002/b/u0003/u001a /u0010/u0000/u001a/u00020/u0001*/b/u0012/u0004/u0012/u00020/u00030/u00022/u0006/u0010/u0004/u001a/u00020/u00032/u0006/u0010/u0005/u001a/u00020/u0003¨/u0006/u0006"}, d2 = {"swap", "", "", "", "index1", "index2", "production sources for module app"})public final class MutableListDemoKt {  public static final void swap(@NotNull List $receiver, int index1, int index2) {  Intrinsics.checkParameterIsNotNull($receiver, "$receiver");   int tmp = ((Number)$receiver.get(index1)).intValue();  $receiver.set(index1, $receiver.get(index2));  $receiver.set(index2, Integer.valueOf(tmp)); }}

從得到的JAVA文件分析,擴展函數的實現非常簡單,它沒有修改接受者類型的成員,僅僅是通過靜態方法來實現的。這樣,我們雖然不必擔心擴展函數會帶來額外的性能消耗,但是它也不會帶來性能上的優化。

3.更復雜的情況

下面來討論一些更特殊的情況。

3.1 當發生繼承時,擴展函數由于本質上是靜態方法,它會嚴格按照參數類型去執行調用,而不會去優先執行或者主動執行父類的方法,如下的例子所示:

open class Aclass B:A()fun A.foo() = "a"fun B.foo() = "b"fun printFoo(a:A){ println(a.foo())}println(B())

上述例子的輸出結果是a,因為擴展函數的入參類型是A,他將會嚴格按照入參類型執行函數調用。

3.2 如果擴展函數和現有的類成員發生沖突,kotlin將會默認使用類成員,這一步選擇是在編譯期處理的,生成的字節碼是將會是調用類成員的方法,如下例子:

class C{  fun foo() {println("Member")}}fun C.foo() {println("Extension")}println(C().foo())

上述的例子將會輸出Member。Kotlin不允許擴展一個已有的成員,原因也很好理解,我們不希望擴展函數成為調用三方sdk的漏洞,不過如果你試圖使用重載的方式創建擴展函數,這樣是可行的。

3.3 Kotlin嚴格區分了可能為空和不為空的入參類型,同樣也應用在擴展函數的中,為了聲明一個可能為空的接受者類型,可以參考如下例子:

fun <T> MutableList<T>?.swap(index1:Int,index2:Int){ if(this == null){  println(null)  return } val tmp = this[index1]  this[index1] = this[index2]  this[index2] = tmp}

3.4 我們有時候還希望能夠添加類似JAVA的“靜態函數”的擴展函數,這時需要借助“伴隨對象(Companion Object)”來實現,如下這個例子:

class D{ companion object{  val m = 1 }}fun D.Companion.foo(){ println("$m in extension")}D.foo()

上面的例子會輸出1 in extension,注意這里調用foo這個擴展函數時,并不需要類D的實例,類似于JAVA的靜態方法。

3.5 如果留意前面的例子,我們會發現kotlin的this語法和JAVA不同,使用范圍更靈活,僅以擴展函數為例,當在擴展函數里調用this時,指代的是接受者類型的實例,那么如果這個擴展函數聲明在一個類內部,我們如何通過this獲取到類的實例呢?可以參考下面的例子:

class E{  fun foo(){  println("foo in Class E") }}class F{  fun foo(){  println("foo in Class F") } fun E.foo2(){    this.foo()    [email protected]() }}E().foo2()

這里使用了kotlin的this指定語法,關鍵字是@,后接指定的類型,上述例子的輸出結果是

foo in Class Efoo in Class F

4. 擴展函數的作用域

一般來說,我們習慣將擴展函數直接定義在包內,例如:

package com.example.extensionfun MutableList<Int>.swap(index1:Int,index2:Int) { val tmp = this[index1] this[index1] = this[index2] this[index2] = tmp}

這樣,在同一個包內可以直接調用改擴展函數,如果我們需要跨包調用擴展函數,我們需要通過import來指明,以上述的例子為例,可以通過import com.example.extension.swap來指定這個擴展函數,也可以通過import com.example.extension.*表示引入該包內的所有擴展函數。得益于Android Studio具備的自動聯想能力,通常不需要我們主動輸入import指令。

有時候,我們也會把擴展函數定義在類的內部,例如:

class G { fun Int.foo(){  println("foo in Class G") }}

這里的Int.foo()是一個定義在類G內部的擴展函數,在這個擴展函數里,我們直接使用Int類型作為接受者類型,因為我們將擴展函數定義在了類的內部,即使我們設置訪問權限為public,它也只能在該類或者該類的子類中被訪問,如果我們設置訪問權限為private,那么在子類中也不能訪問這個擴展函數。

5. 擴展函數的實際應用

5.1 Utils工具類

在JAVA中,我們習慣將工具類命名成*Utils,例如FileUtils,StringUtils等等,著名的java.util.Collections也是這么實現的。調用這些方法的時候,總覺得這些類名礙手礙腳的,例如這樣:

// JavaCollections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list));Collections.max(list));

通過靜態引用,能讓情況看起來好一點,例如這樣:

// Javaswap(list, binarySearch(list, max(otherList)), max(list));

但是這樣既沒有IDE的自動聯想提示,方法調用的主體也顯得不明確。如果能做成下面這樣就好了:

// Javalist.swap(list.binarySearch(otherList.max()), list.max());

但是list是JAVA默認的基礎類,在JAVA語言里,如果不使用繼承,肯定是沒法做到這樣的,而在Kotlin中就可以借助擴展函數來實現啦。

5.2 Android View 膠水代碼

回到最開始的例子,對于Android開發來說,對findViewById()這個方法一定不會陌生,為了獲取一個View對象,我們總得先調用findViewById()然后再執行類型轉換,這樣無意義的膠水代碼讓Activity或者Fragment顯得臃腫無比,例如:

// JAVApublic class MainJavaActivity extends Activity {  @Override public void onCreate(@Nullable Bundle savedInstanceState) {    super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  TextView label = (TextView) findViewById(R.id.label);  Button btn = (Button) findViewById(R.id.btn);  label.setText("hello");  label.setOnClickListener(new View.OnClickListener() {      @Override   public void onClick(View v) {    Log.d("Glen","onClick TextView");   }  });  btn.setOnClickListener(new View.OnClickListener(){       @Override   public void onClick(View v) {    Log.d("Glen","onClick Button");   }  }); }}

我們考慮利用擴展函數結合泛型,避免頻繁的類型轉換,擴展函數定義如下:

//kotlinfun <T : View> Activity.find(@IdRes id: Int): T {  return findViewById(id) as T}

調用的時候,如下:

// Kotlin... TextView label = find(R.id.label); Button btn = find(R.id.btn);...

只是我們還是需要獲取到label,btn,這樣無意義的中間變量,如果在Int類上擴展,可以直接對R.id.*操作,這樣更直接,再結合高階函數,函數定義如下:

//Kotlinfun Int.setText(str:String){ val label = find<TextView>(this).apply {  text = str }}fun Int.onClick(click: ()->Unit){ val tmp = find<View>(this).apply {  setOnClickListener{   click()  } }}

我們就可以這樣調用:

//KotlinR.id.label.setText("hello")R.id.label.onClick { Log.d("Glen","onClick TextView") }R.id.btn.onClick { Log.d("Glen","onClick Button") }

通常這些擴展函數可以放到基類中,根據擴展函數的作用域知識,我們可以在所有子類中都調用到這些方法,所以kotlin的Activity可以寫成:

// Kotlinclass MainKotlinActivity:KotlinBaseActivity() { override fun onCreate(savedInstanceState: Bundle?) {  super.onCreate(savedInstanceState)  setContentView(R.layout.activity_main)  R.id.label.setText("hello")  R.id.label.onClick { Log.d("Glen","onClick TextView") }  R.id.btn.onClick { Log.d("Glen","onClick Button") } }}

從原來JAVA冗余的20多行代碼,精簡到只需要3行代碼,而且代碼可讀性更高,更加直觀,這便是函數式編程語言Kotlin的強大威力。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到kotlin教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 成熟女人特级毛片www免费 | 欧美精品一区二区三区在线播放 | 91久久夜色精品国产网站 | 国产免费视频一区二区裸体 | 激情视频免费看 | 91短视频在线 | 国产精品二区高清在线 | 艹男人的日日夜夜 | 超碰97国产在线 | 美国一级毛片片aa久久综合 | 欧美视频国产 | 久草视频免费 | 正在播放91 | 久久国产成人精品国产成人亚洲 | 久久久久久久久久久av | 欧美人与禽性xxxxx杂性 | 免费人成在线播放 | 亚洲小视频在线观看,com | 欧美精品一区二区中文字幕 | 天海翼四虎精品正在播放 | 看片一区二区三区 | 亚洲成人欧美在线 | 韩毛片| 国产毛片自拍 | 国产毛片毛片毛片 | 亚洲精品午夜电影 | 黄视频免费在线观看 | 91美女福利视频 | 成年人视频免费看 | 免看黄大片aa | 国产精品午夜未成人免费观看 | 国产精品高潮99久久久久久久 | hdbbwsexvideo| 久久精品视频首页 | 国产九九热 | 亚洲国产色婷婷 | 欧洲狠狠鲁| 国产精品一区二区三区在线看 | 精品一区在线视频 | 久久久久亚洲视频 | 视频一区二区三区中文字幕 |