真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Kotlin中擴(kuò)展函數(shù)的實(shí)現(xiàn)機(jī)制是什么

這篇文章將為大家詳細(xì)講解有關(guān)Kotlin中擴(kuò)展函數(shù)的實(shí)現(xiàn)機(jī)制是什么,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

創(chuàng)新互聯(lián)公司專注于彰武企業(yè)網(wǎng)站建設(shè),自適應(yīng)網(wǎng)站建設(shè),商城網(wǎng)站定制開(kāi)發(fā)。彰武網(wǎng)站建設(shè)公司,為彰武等地區(qū)提供建站服務(wù)。全流程專業(yè)公司,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)

// JAVA,20多行代碼,充斥著findViewById,類型轉(zhuǎn)換,匿名內(nèi)部類這樣的無(wú)意義代碼

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");
  }
 });
 }
}

再來(lái)看Kotlin

// Kotlin,沒(méi)有了冗余的findViewById,我們可以直接對(duì)資源id進(jìn)行操作,也不需要匿名內(nèi)部類的聲明,更關(guān)注函數(shù)的實(shí)現(xiàn)本身,拋棄了復(fù)雜的格式
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") }
 }
}

實(shí)現(xiàn)這些需要借助Kotlin的擴(kuò)展函數(shù)與高階函數(shù),本文主要介紹一下擴(kuò)展函數(shù)。

什么是擴(kuò)展函數(shù)?

擴(kuò)展函數(shù)數(shù)是指在一個(gè)類上增加一種新的行為,甚至我們沒(méi)有這個(gè)類代碼的訪問(wèn)權(quán)限。這是一個(gè)在缺少有用函數(shù)的類上擴(kuò)展的方法,Kotlin能夠?yàn)槲覀冏龅侥切┝钊岁P(guān)注的事情,而這些Java做不到。

在Java中,通常會(huì)實(shí)現(xiàn)很多帶有static方法的工具類,而Kotlin中擴(kuò)展函數(shù)的一個(gè)優(yōu)勢(shì)是我們不需要在調(diào)用方法的時(shí)候把整個(gè)對(duì)象當(dāng)作參數(shù)傳入,它表現(xiàn)得就像是屬于這個(gè)類的一樣,而且我們可以使用this關(guān)鍵字和調(diào)用所有public方法。

1. Kotlin 擴(kuò)展函數(shù)與擴(kuò)展屬性(Kotlin Extensions)

Kotlin 能夠擴(kuò)展一個(gè)類的新功能而無(wú)需繼承該類,或者對(duì)任意的類使用像“裝飾者(Decorator)”這樣的設(shè)計(jì)模式。這些都是通過(guò)叫做“擴(kuò)展(extensions)”的特殊聲明實(shí)現(xiàn)的。Kotlin擴(kuò)展聲明既支持?jǐn)U展函數(shù)也支持?jǐn)U展屬性,本文主要討論擴(kuò)展函數(shù),至于擴(kuò)展屬性實(shí)現(xiàn)的機(jī)制類似。

擴(kuò)展函數(shù)的聲明非常簡(jiǎn)單,他的關(guān)鍵字是.,此外我們需要一個(gè)“接受者類型(recievier type)”來(lái)作為他的前綴。以類MutableList為例,現(xiàn)在為它擴(kuò)展一個(gè)swap方法,如下:

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

MutableList是kotlin提供的基礎(chǔ)庫(kù)collection中的List容器類,這里在聲明里作為“接受者類型”,.作為聲明關(guān)鍵字,swap是擴(kuò)展函數(shù)名,其余和Kotlin聲明一個(gè)普通函數(shù)并無(wú)區(qū)別。

額外提一句,Kotlin的this語(yǔ)法要比JAVA更靈活,這里擴(kuò)展函數(shù)體里的this代表的是接受者類型對(duì)象。

如果我們想要調(diào)用這個(gè)擴(kuò)展函數(shù),可以這樣:

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

2. Kotlin擴(kuò)展函數(shù)是怎么實(shí)現(xiàn)的

擴(kuò)展函數(shù)的調(diào)用看起來(lái)就像是原生方法一樣自然,使用起來(lái)也非常順手,但是這樣的方法會(huì)不會(huì)帶來(lái)性能方面的掣肘呢?有必要探究一下Kotlin是如何實(shí)現(xiàn)擴(kuò)展函數(shù)的,直接分析Kotlin源碼難度還是挺大,還好Android Studio提供了一些工具,我們可以通過(guò)Kotlin ByteCode指令,查看Kotlin語(yǔ)言轉(zhuǎn)換的字節(jié)碼文件,仍以MutableList,swap為例,轉(zhuǎn)換為字節(jié)碼之后的文件如下:

// ================com/example/glensun/demo/extension/MutableListDemoKt.class =================
// class version 50.0 (50)
// access flags 0x31

public final class com/example/glensun/demo/extension/MutableListDemoKt { 

 // access flags 0x19
 // signature (Ljava/util/List;II)V
 // declaration: void swap(java.util.List, 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 =================

這里的字節(jié)碼已經(jīng)相當(dāng)直觀,更令人驚喜的是Android Studio還具備將字節(jié)碼轉(zhuǎn)為JAVA文件的能力,點(diǎn)擊上面的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文件分析,擴(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)額外的性能消耗,但是它也不會(huì)帶來(lái)性能上的優(yōu)化。

3.更復(fù)雜的情況

下面來(lái)討論一些更特殊的情況。

3.1 當(dāng)發(fā)生繼承時(shí),擴(kuò)展函數(shù)由于本質(zhì)上是靜態(tài)方法,它會(huì)嚴(yán)格按照參數(shù)類型去執(zhí)行調(diào)用,而不會(huì)去優(yōu)先執(zhí)行或者主動(dòng)執(zhí)行父類的方法,如下的例子所示:

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

上述例子的輸出結(jié)果是a,因?yàn)閿U(kuò)展函數(shù)的入?yún)㈩愋褪茿,他將會(huì)嚴(yán)格按照入?yún)㈩愋蛨?zhí)行函數(shù)調(diào)用。

3.2 如果擴(kuò)展函數(shù)和現(xiàn)有的類成員發(fā)生沖突,kotlin將會(huì)默認(rèn)使用類成員,這一步選擇是在編譯期處理的,生成的字節(jié)碼是將會(huì)是調(diào)用類成員的方法,如下例子:

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

上述的例子將會(huì)輸出Member。Kotlin不允許擴(kuò)展一個(gè)已有的成員,原因也很好理解,我們不希望擴(kuò)展函數(shù)成為調(diào)用三方sdk的漏洞,不過(guò)如果你試圖使用重載的方式創(chuàng)建擴(kuò)展函數(shù),這樣是可行的。

3.3 Kotlin嚴(yán)格區(qū)分了可能為空和不為空的入?yún)㈩愋?,同樣也?yīng)用在擴(kuò)展函數(shù)的中,為了聲明一個(gè)可能為空的接受者類型,可以參考如下例子:

fun  MutableList?.swap(index1:Int,index2:Int){
 if(this == null){
  println(null)
  return
 }

 val tmp = this[index1] 
 this[index1] = this[index2] 
 this[index2] = tmp
}

3.4 我們有時(shí)候還希望能夠添加類似JAVA的“靜態(tài)函數(shù)”的擴(kuò)展函數(shù),這時(shí)需要借助“伴隨對(duì)象(Companion Object)”來(lái)實(shí)現(xiàn),如下這個(gè)例子:

class D{
 companion object{
  val m = 1
 }
}

fun D.Companion.foo(){
 println("$m in extension")
}

D.foo()

上面的例子會(huì)輸出1 in extension,注意這里調(diào)用foo這個(gè)擴(kuò)展函數(shù)時(shí),并不需要類D的實(shí)例,類似于JAVA的靜態(tài)方法。

3.5 如果留意前面的例子,我們會(huì)發(fā)現(xiàn)kotlin的this語(yǔ)法和JAVA不同,使用范圍更靈活,僅以擴(kuò)展函數(shù)為例,當(dāng)在擴(kuò)展函數(shù)里調(diào)用this時(shí),指代的是接受者類型的實(shí)例,那么如果這個(gè)擴(kuò)展函數(shù)聲明在一個(gè)類內(nèi)部,我們?nèi)绾瓮ㄟ^(guò)this獲取到類的實(shí)例呢?可以參考下面的例子:

class E{ 
 fun foo(){
  println("foo in Class E")
 }

}
class F{ 
 fun foo(){
  println("foo in Class F")
 }

 fun E.foo2(){  
  this.foo()  
  this@F.foo()
 }
}

E().foo2()

這里使用了kotlin的this指定語(yǔ)法,關(guān)鍵字是@,后接指定的類型,上述例子的輸出結(jié)果是

foo in Class E
foo in Class F

4. 擴(kuò)展函數(shù)的作用域

一般來(lái)說(shuō),我們習(xí)慣將擴(kuò)展函數(shù)直接定義在包內(nèi),例如:

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

這樣,在同一個(gè)包內(nèi)可以直接調(diào)用改擴(kuò)展函數(shù),如果我們需要跨包調(diào)用擴(kuò)展函數(shù),我們需要通過(guò)import來(lái)指明,以上述的例子為例,可以通過(guò)import com.example.extension.swap來(lái)指定這個(gè)擴(kuò)展函數(shù),也可以通過(guò)import com.example.extension.*表示引入該包內(nèi)的所有擴(kuò)展函數(shù)。得益于Android Studio具備的自動(dòng)聯(lián)想能力,通常不需要我們主動(dòng)輸入import指令。

有時(shí)候,我們也會(huì)把擴(kuò)展函數(shù)定義在類的內(nèi)部,例如:

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

這里的Int.foo()是一個(gè)定義在類G內(nèi)部的擴(kuò)展函數(shù),在這個(gè)擴(kuò)展函數(shù)里,我們直接使用Int類型作為接受者類型,因?yàn)槲覀儗U(kuò)展函數(shù)定義在了類的內(nèi)部,即使我們?cè)O(shè)置訪問(wèn)權(quán)限為public,它也只能在該類或者該類的子類中被訪問(wèn),如果我們?cè)O(shè)置訪問(wèn)權(quán)限為private,那么在子類中也不能訪問(wèn)這個(gè)擴(kuò)展函數(shù)。

5. 擴(kuò)展函數(shù)的實(shí)際應(yīng)用

5.1 Utils工具類

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

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

通過(guò)靜態(tài)引用,能讓情況看起來(lái)好一點(diǎn),例如這樣:

// Java
swap(list, binarySearch(list, max(otherList)), max(list));

但是這樣既沒(méi)有IDE的自動(dòng)聯(lián)想提示,方法調(diào)用的主體也顯得不明確。如果能做成下面這樣就好了:

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

但是list是JAVA默認(rèn)的基礎(chǔ)類,在JAVA語(yǔ)言里,如果不使用繼承,肯定是沒(méi)法做到這樣的,而在Kotlin中就可以借助擴(kuò)展函數(shù)來(lái)實(shí)現(xiàn)啦。

5.2 Android View 膠水代碼

回到最開(kāi)始的例子,對(duì)于Android開(kāi)發(fā)來(lái)說(shuō),對(duì)findViewById()這個(gè)方法一定不會(huì)陌生,為了獲取一個(gè)View對(duì)象,我們總得先調(diào)用findViewById()然后再執(zhí)行類型轉(zhuǎn)換,這樣無(wú)意義的膠水代碼讓Activity或者Fragment顯得臃腫無(wú)比,例如:

// JAVA

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");
   }
  });
 }
}

我們考慮利用擴(kuò)展函數(shù)結(jié)合泛型,避免頻繁的類型轉(zhuǎn)換,擴(kuò)展函數(shù)定義如下:

//kotlin
fun  Activity.find(@IdRes id: Int): T { 
 return findViewById(id) as T
}

調(diào)用的時(shí)候,如下:

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

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

//Kotlin
fun Int.setText(str:String){
 val label = find(this).apply {
  text = str
 }
}

fun Int.onClick(click: ()->Unit){
 val tmp = find(this).apply {
  setOnClickListener{
   click()
  }
 }
}

我們就可以這樣調(diào)用:

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

通常這些擴(kuò)展函數(shù)可以放到基類中,根據(jù)擴(kuò)展函數(shù)的作用域知識(shí),我們可以在所有子類中都調(diào)用到這些方法,所以kotlin的Activity可以寫(xiě)成:

// Kotlin
class 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") }
 }
}

關(guān)于Kotlin中擴(kuò)展函數(shù)的實(shí)現(xiàn)機(jī)制是什么就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。


網(wǎng)站題目:Kotlin中擴(kuò)展函數(shù)的實(shí)現(xiàn)機(jī)制是什么
網(wǎng)頁(yè)URL:http://weahome.cn/article/poejis.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部