版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
【移動應(yīng)用開發(fā)技術(shù)】Kotlin中擴(kuò)展函數(shù)的實(shí)現(xiàn)機(jī)制是什么
這篇文章將為大家詳細(xì)講解有關(guān)Kotlin中擴(kuò)展函數(shù)的實(shí)現(xiàn)機(jī)制是什么,文章內(nèi)容質(zhì)量較高,因此在下分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。//
JAVA,20多行代碼,充斥著findViewById,類型轉(zhuǎn)換,匿名內(nèi)部類這樣的無意義代碼
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進(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ù)是指在一個類上增加一種新的行為,甚至我們沒有這個類代碼的訪問權(quán)限。這是一個在缺少有用函數(shù)的類上擴(kuò)展的方法,Kotlin能夠?yàn)槲覀冏龅侥切┝钊岁P(guān)注的事情,而這些Java做不到。在Java中,通常會實(shí)現(xiàn)很多帶有static方法的工具類,而Kotlin中擴(kuò)展函數(shù)的一個優(yōu)勢是我們不需要在調(diào)用方法的時候把整個對象當(dāng)作參數(shù)傳入,它表現(xiàn)得就像是屬于這個類的一樣,而且我們可以使用this關(guān)鍵字和調(diào)用所有public方法。1.Kotlin擴(kuò)展函數(shù)與擴(kuò)展屬性(KotlinExtensions)Kotlin能夠擴(kuò)展一個類的新功能而無需繼承該類,或者對任意的類使用像“裝飾者(Decorator)”這樣的設(shè)計(jì)模式。這些都是通過叫做“擴(kuò)展(extensions)”的特殊聲明實(shí)現(xiàn)的。Kotlin擴(kuò)展聲明既支持?jǐn)U展函數(shù)也支持?jǐn)U展屬性,本文主要討論擴(kuò)展函數(shù),至于擴(kuò)展屬性實(shí)現(xiàn)的機(jī)制類似。擴(kuò)展函數(shù)的聲明非常簡單,他的關(guān)鍵字是.,此外我們需要一個“接受者類型(recieviertype)”來作為他的前綴。以類MutableList<Int>為例,現(xiàn)在為它擴(kuò)展一個swap方法,如下:fun
MutableList<Int>.swap(index1:Int,index2:Int)
{
val
tmp
=
this[index1]
this[index1]
=
this[index2]
this[index2]
=
tmp
}MutableList<T>是kotlin提供的基礎(chǔ)庫collection中的List容器類,這里在聲明里作為“接受者類型”,.作為聲明關(guān)鍵字,swap是擴(kuò)展函數(shù)名,其余和Kotlin聲明一個普通函數(shù)并無區(qū)別。額外提一句,Kotlin的this語法要比JAVA更靈活,這里擴(kuò)展函數(shù)體里的this代表的是接受者類型對象。如果我們想要調(diào)用這個擴(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)用看起來就像是原生方法一樣自然,使用起來也非常順手,但是這樣的方法會不會帶來性能方面的掣肘呢?有必要探究一下Kotlin是如何實(shí)現(xiàn)擴(kuò)展函數(shù)的,直接分析Kotlin源碼難度還是挺大,還好AndroidStudio提供了一些工具,我們可以通過KotlinByteCode指令,查看Kotlin語言轉(zhuǎn)換的字節(jié)碼文件,仍以MutableList<Int>,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<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/NValue
()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)直觀,更令人驚喜的是AndroidStudio還具備將字節(jié)碼轉(zhuǎn)為JAVA文件的能力,點(diǎn)擊上面的Decompile按鈕,可以得到如下JAVA代碼:import
java.util.List;
import
kotlin.Metadata;
import
ernal.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)非常簡單,它沒有修改接受者類型的成員,僅僅是通過靜態(tài)方法來實(shí)現(xiàn)的。這樣,我們雖然不必?fù)?dān)心擴(kuò)展函數(shù)會帶來額外的性能消耗,但是它也不會帶來性能上的優(yōu)化。3.更復(fù)雜的情況下面來討論一些更特殊的情況。3.1當(dāng)發(fā)生繼承時,擴(kuò)展函數(shù)由于本質(zhì)上是靜態(tài)方法,它會嚴(yán)格按照參數(shù)類型去執(zhí)行調(diào)用,而不會去優(yōu)先執(zhí)行或者主動執(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)㈩愋褪茿,他將會嚴(yán)格按照入?yún)㈩愋蛨?zhí)行函數(shù)調(diào)用。3.2如果擴(kuò)展函數(shù)和現(xiàn)有的類成員發(fā)生沖突,kotlin將會默認(rèn)使用類成員,這一步選擇是在編譯期處理的,生成的字節(jié)碼是將會是調(diào)用類成員的方法,如下例子:class
C{
fun
foo()
{println("Member")}
}
fun
C.foo()
{println("Extension")}
println(C().foo())上述的例子將會輸出Member。Kotlin不允許擴(kuò)展一個已有的成員,原因也很好理解,我們不希望擴(kuò)展函數(shù)成為調(diào)用三方sdk的漏洞,不過如果你試圖使用重載的方式創(chuàng)建擴(kuò)展函數(shù),這樣是可行的。3.3Kotlin嚴(yán)格區(qū)分了可能為空和不為空的入?yún)㈩愋?,同樣也?yīng)用在擴(kuò)展函數(shù)的中,為了聲明一個可能為空的接受者類型,可以參考如下例子: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的“靜態(tài)函數(shù)”的擴(kuò)展函數(shù),這時需要借助“伴隨對象(CompanionObject)”來實(shí)現(xiàn),如下這個例子:class
D{
companion
object{
val
m
=
1
}
}
fun
D.Companion.foo(){
println("$m
in
extension")
}
D.foo()上面的例子會輸出1inextension,注意這里調(diào)用foo這個擴(kuò)展函數(shù)時,并不需要類D的實(shí)例,類似于JAVA的靜態(tài)方法。3.5如果留意前面的例子,我們會發(fā)現(xiàn)kotlin的this語法和JAVA不同,使用范圍更靈活,僅以擴(kuò)展函數(shù)為例,當(dāng)在擴(kuò)展函數(shù)里調(diào)用this時,指代的是接受者類型的實(shí)例,那么如果這個擴(kuò)展函數(shù)聲明在一個類內(nèi)部,我們?nèi)绾瓮ㄟ^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指定語法,關(guān)鍵字是@,后接指定的類型,上述例子的輸出結(jié)果是foo
in
Class
E
foo
in
Class
F4.擴(kuò)展函數(shù)的作用域一般來說,我們習(xí)慣將擴(kuò)展函數(shù)直接定義在包內(nèi),例如:package
com.example.extension
fun
MutableList<Int>.swap(index1:Int,index2:Int)
{
val
tmp
=
this[index1]
this[index1]
=
this[index2]
this[index2]
=
tmp
}這樣,在同一個包內(nèi)可以直接調(diào)用改擴(kuò)展函數(shù),如果我們需要跨包調(diào)用擴(kuò)展函數(shù),我們需要通過import來指明,以上述的例子為例,可以通過importcom.example.extension.swap來指定這個擴(kuò)展函數(shù),也可以通過importcom.example.extension.*表示引入該包內(nèi)的所有擴(kuò)展函數(shù)。得益于AndroidStudio具備的自動聯(lián)想能力,通常不需要我們主動輸入import指令。有時候,我們也會把擴(kuò)展函數(shù)定義在類的內(nèi)部,例如:class
G
{
fun
Int.foo(){
println("foo
in
Class
G")
}
}這里的Int.foo()是一個定義在類G內(nèi)部的擴(kuò)展函數(shù),在這個擴(kuò)展函數(shù)里,我們直接使用Int類型作為接受者類型,因?yàn)槲覀儗U(kuò)展函數(shù)定義在了類的內(nèi)部,即使我們設(shè)置訪問權(quán)限為public,它也只能在該類或者該類的子類中被訪問,如果我們設(shè)置訪問權(quán)限為private,那么在子類中也不能訪問這個擴(kuò)展函數(shù)。5.擴(kuò)展函數(shù)的實(shí)際應(yīng)用5.1Utils工具類在JAVA中,我們習(xí)慣將工具類命名成*Utils,例如FileUtils,StringUtils等等,著名的java.util.Collections也是這么實(shí)現(xiàn)的。調(diào)用這些方法的時候,總覺得這些類名礙手礙腳的,例如這樣://
Java
Collections.swap(list,
Collections.binarySearch(list,
Collections.max(otherList)),
Collections.max(list));
Collections.max(list));通過靜態(tài)引用,能讓情況看起來好一點(diǎn),例如這樣://
Java
swap(list,
binarySearch(list,
max(otherList)),
max(list));但是這樣既沒有IDE的自動聯(lián)想提示,方法調(diào)用的主體也顯得不明確。如果能做成下面這樣就好了://
Java
list.swap(list.binarySearch(otherList.max()),
list.max());但是list是JAVA默認(rèn)的基礎(chǔ)類,在JAVA語言里,如果不使用繼承,肯定是沒法做到這樣的,而在Kotlin中就可以借助擴(kuò)展函數(shù)來實(shí)現(xiàn)啦。5.2AndroidView膠水代碼回到最開始的例子,對于Android開發(fā)來說,對findViewById()這個方法一定不會陌生,為了獲取一個View對象,我們總得先調(diào)用findViewById()然后再執(zhí)行類型轉(zhuǎn)換,這樣無意義的膠水代碼讓Activity或者Fragment顯得臃腫無比,例如://
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
<T
:
View>
Activity.find(@IdRes
id:
Int):
T
{
return
findViewById(id)
as
T
}調(diào)用的時候,如下://
Kotlin
...
TextView
label
=
fin
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025-2030中國輕型自行車行業(yè)前景預(yù)測及供需現(xiàn)狀分析研究報(bào)告
- 2025-2030中國黑加侖葡萄干市場消費(fèi)規(guī)模及投資策略深度調(diào)查研究報(bào)告
- 建筑工地安全管理及封樣制度解析
- 安全生產(chǎn)法培訓(xùn)心得與體會
- 2025-2030照相機(jī)行業(yè)市場深度調(diào)研及發(fā)展趨勢與投資前景預(yù)測研究報(bào)告
- 2025-2030潛水旅游行業(yè)安全標(biāo)準(zhǔn)提升與區(qū)域發(fā)展策略分析報(bào)告
- 2025-2030消費(fèi)級機(jī)器人人機(jī)交互技術(shù)突破與場景落地評估報(bào)告
- 2025-2030消費(fèi)級無人機(jī)監(jiān)管政策空域管理改革方向分析評估報(bào)告
- 2025-2030消費(fèi)級無人機(jī)市場增長空間與競爭格局預(yù)測報(bào)告
- 2025-2030消費(fèi)級基因檢測產(chǎn)品市場教育程度與增長潛力報(bào)告
- 2026院感知識考試題及答案
- DL∕T 5210.6-2019 電力建設(shè)施工質(zhì)量驗(yàn)收規(guī)程 第6部分:調(diào)整試驗(yàn)
- 2024年安徽省高考地理試卷(真題+答案)
- 新生兒機(jī)械通氣指南
- 裝修民事糾紛調(diào)解協(xié)議書
- 2023年P(guān)CB工程師年度總結(jié)及來年計(jì)劃
- 森林防火工作先進(jìn)個人事跡材料
- MH5006-2015民用機(jī)場飛行區(qū)水泥混凝土道面面層施工技術(shù)規(guī)范
- 施工交通疏導(dǎo)方案
- 1例低血糖昏迷的護(hù)理查房
- 智慧校園網(wǎng)投資建設(shè)運(yùn)營方案
評論
0/150
提交評論