kotlin
將一個activity的java文件轉(zhuǎn)為kotlin文件
最簡單的轉(zhuǎn)化方式
android stutdio支持一鍵轉(zhuǎn)化的方式继阻,通過快捷鍵ctrl +alt + shift + k將整個java文件轉(zhuǎn)化為kt文件宏蛉,不過可能需要自己解決一些代碼不一致的沖突囱修,而且有些時候轉(zhuǎn)化后的代碼和我們直接寫的代碼還是有差別的口柳。
環(huán)境配置
kotlin庫引入
android studio3.0默認支持kotlin芥挣,雖然是默認支持kotlin,但也是需要增加一些配置扶檐,不過這個as已經(jīng)給我們做好了智能提示和自動添加的功能葫松。在我們第一次創(chuàng)建出kt文件的時候瓦糕,將會有一個kotlin的配置提示,我們只需要點擊configure腋么,然后選擇全局配置咕娄,那么as將會在gradle中添加上kotlin的依賴
[圖片上傳失敗...(image-4852fe-1542511738777)]
gradle文件如下所示:
[圖片上傳失敗...(image-17de9f-1542511738777)]
這樣我們就使用kotlin的代碼庫了。
kotlin extension庫引入
之前項目中用的是view的綁定庫是ButterKnife珊擂,就個人而言圣勒,還是覺得挺好用的,畢竟綁定View的代碼都能自動生成摧扇,不過如果在kt文件中圣贸,不能直接引入這個庫,需要重新引入kotlin中的ButterKnife庫https://github.com/JakeWharton/kotterknife扛稽,不過我們可以不用這個庫了吁峻,因為kotlin給我們提供了一套更為簡便的庫,我們只需要在gradle中引入kotlin-extension在张,并且在kt文件中import相對應的xml用含,就能直接通過id的方式引用view。
//在kt文件中
import kotlinx.android.synthetic.main.activity_member_center.*
//在gradle文件中
apply plugin: 'kotlin-android-extensions'
比如說我們在xml中有這樣一個View:
<android.support.v7.widget.RecyclerView
android:id="@+id/memberRv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/member_center_tool_bar"
android:layout_centerHorizontal="true" />
這樣我們在kt文件中帮匾,可以像下面的方式這樣調(diào)用:
private fun initRv() {
adapter = MemberInfoAdapter(this, null)
memberRv.layoutManager = LinearLayoutManager(this)
memberRv.itemAnimator?.changeDuration = 0
memberRv.adapter = adapter
}
這個memberRv就是RecyclerView在xml中指定的id啄骇。
一些常用用法
參數(shù)定義
修飾符
在kotlin中,聲明所有變量都需要3個關鍵字修飾瘟斜,var缸夹、val和const。在kotlin中是沒有final修飾符的哼转,final修飾符在kotlin中其實就相當于val
- var:主要是用來修飾可變變量的明未。
- val:主要是用來修飾不可變變量的,其實也不能說是不可變壹蔓,更好的稱呼是叫做只讀變量,val在修飾變量的時候可以不初始化猫态,但是它不能被賦值佣蓉,在java中披摄,用final修飾的變量必須要初始化,否則編譯不能通過勇凭。舉個val修飾不初始化的例子:
class a { var c = 1; val b: String fun getB(): String { return c > 3 ? "haha" : "xixi" } fun add() { c++ } }
- const:其實const就和java中的static final一致了疚膊,不能放在局部方法中,在編譯期間必須確定變量值虾标。
靜態(tài)變量及靜態(tài)方法
在java中寓盗,靜態(tài)屬性和靜態(tài)方法只需要一個static屬性就能搞定了,但是在kotlin璧函,它有一個單獨的塊來標識靜態(tài)塊的初始化傀蚌,companion object:
companion object {
val intentFrom: String = "intent_extra"
fun startMemberCenterActivity(context: Context, launchMemberCenter: LaunchMemberCenter) {
var intent = Intent(context, TestAc::class.java)
intent.putExtra(intentFrom, launchMemberCenter)
if (context is Activity) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
}
}
這個代碼塊有幾個需要注意的點,
- 在kt定義Intent中蘸吓,class的指定需要已類名::class.java的方式指定善炫,否則在log中會提示找不到activity
- 在這個靜態(tài)塊中,定義的變量和方法都是靜態(tài)的
- 在外部如果需要訪問這個塊的方法库继,如果是kotlin文件訪問箩艺,可以直接通過類名.的方式調(diào)用,如果是java文件訪問宪萄,需要通過類名.companion.的方式調(diào)用:
//在java文件中調(diào)用
public void start() {
TestAc.Companion.startMemberCenterActivity(context, this);
}
//在kt文件中調(diào)用
public void start() {
TestAc.startMemberCenterActivity(context, this);
}
kotlin的空指針安全
在變量定義時艺谆,我們可以通過kotlin特有的方式來標識變量是否可空:
private var adapter: MemberInfoAdapter? = null
只需要的變量類型后面添加?,就表示這個變量是可以為null的拜英,如果不添加擂涛,默認這個變量不能為null。一旦我們添加了?聊记,在后續(xù)的代碼中撒妈,如果有引用到這個變量的地方,如果沒有做判空處理將會編譯不過排监,這個判空處理還是挺方便的狰右,
override fun refreshDiscoveryList(mixFindInfoList: MutableList<MixFindInfo>?) {
adapter?.notifyMixFindInfoChange(mixFindInfoList)
}
Equality check should be used instead of elvis for nullable boolean check
在調(diào)用時,如果adaper為空了舆床,將不會執(zhí)行后續(xù)的操作棋蚌。
今天遇到了一個符號:
var xie = a ?: ""
這個?:是kotlin的elvis用法,屬于兩目運算符挨队,就是簡單的if谷暮、else縮寫,
當a不為null盛垦,取a的值湿弦,當a為null,取""
接口使用
kotlin接口和java接口的使用區(qū)別
class TestAc : BaseMvpActivity<MemberCenterPresent>(), MemberCenterPresent.MemberInfoChange,
KKAccountManager.KKAccountChangeListener, View.OnClickListener {
其中BaseMvpActivity繼承腾夯,而其他三個都是接口繼承颊埃。
- java接口需要用implements關鍵字實現(xiàn)蔬充,而kotlin實現(xiàn)的方式和繼承一樣
- java接口能夠直接通過匿名內(nèi)部類創(chuàng)建出來,而kotlin也需要先將接口對象創(chuàng)建出來
when表達式
在kotlin中班利,用when表達式將將java中的switch替換掉了饥漫。
最簡單的形式如下:
override fun onClick(v: View?) {
when (v?.id) {
R.id.icBack -> finish()
R.id.btnOpenLayout -> btnOpenLayoutClick()
else -> {
}
}
而且,when里面的條件判斷可以加入比較復雜的判斷罗标,如下:
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
繼承
kotlin中當含有非空的構造函數(shù)得繼承庸队,有以下這幾種方式:
class MemberCenterAdapter(context: Context?, mixFindInfoList: ArrayList<MixFindInfo>?) : TopicTabListAdapter(context, mixFindInfoList)
相當于子類的構造器聲明就放在了class的定義上,然后會對應父類相應的構造器闯割。還有另外一種實現(xiàn)方式彻消,主要用于多個構造器的聲明。
constructor(context: Context?, mixFindInfoList: ArrayList<MixFindInfo>?): super(context, mixFindInfoList)
列表
列表元素訪問
在kotlin中纽谒,推薦我們使用下標的方式來訪問元素证膨,就像在訪問數(shù)組一樣。
private var itemTypeList: ArrayList<Int> = ArrayList()
itemTypeList[2] = ITEM_TYPE_ACTIVITY
列表元素遍歷
對于kotlin中的列表遍歷鼓黔,增加了許多遍歷方式央勒。最簡單的如下所示:
//kotlin
for (i in startIndex..mixFindInfoList!!.size )
//java
for(int i = startIndex; i < mixFindInfoList.size(); i++)
在kotlin中,推薦我們使用until的方式進行遍歷澳化,這樣崔步,我們處理起來也就像是在處理流一樣。
// a until b表示從a到b進行遍歷缎谷,并且將遍歷的值傳遞給下游井濒,
// map將上游傳遞過來的值進行轉(zhuǎn)化,并傳遞給下游
// forEach對轉(zhuǎn)化后的值進行處理
(startIndex until mixFindInfoList!!.size)
.map { mixFindInfoList[it] }
.forEach {
itemTypeList.add(it.item_type)
}
三目運算符
之前在java中用慣了三目運算符列林,看著kotlin的三目運算符挺不習慣的瑞你。
//java, 如果a為true,則取b希痴,否則取c
a ? b : c
//kotlin, 利用if else組成三目運算符者甲,但是不需要括號
if(a) b else c
函數(shù)中流程語句的返回值
在kotlin中,如果多個分支語句都返回值砌创,那么我們可以將返回值放在分支語句的外側(cè)虏缸。
//最原始的版本
when (position) {
0, 2 -> return null
1 -> return Utility.getSafely(mixFindInfoList, 0)
else -> {
return Utility.getSafely<MixFindInfo>(mixFindInfoList, position - 2)
}
}
優(yōu)化后的版本:
return when (position) {
0, 2 -> null
1 -> Utility.getSafely(mixFindInfoList, 0)
else -> {
Utility.getSafely<MixFindInfo>(mixFindInfoList, position - 2) }
}
這種時候,就能夠返回每一個when分支對應的值
對于if else語句也能這么使用
return if (activityListExist()) {
if (vipBannerExist()) {
when (position) {
0, 2 -> null
1 -> Utility.getSafely(mixFindInfoList, 0)
else -> {
Utility.getSafely<MixFindInfo>(mixFindInfoList, position - 2)
}
}
} else {
if (position == 0 || position == 1) {
null
} else {
Utility.getSafely<MixFindInfo>(mixFindInfoList, position - 2)
}
}
} else {
if (position == 0) {
null
} else {
Utility.getSafely<MixFindInfo>(mixFindInfoList, position - 1)
}
}
}
這種時候嫩实,每一種判斷的分支對應一個值刽辙,返回命中的分支對應的值
類型判斷和類型轉(zhuǎn)換
java中類型判斷和轉(zhuǎn)化:
if (holder instance of MemberVipHeaderHolder) {
((MemberVipHeaderHolder)holder).bindData(title, TYPE_VIP_HEADER, isVip, isBtnShow);
}
java中利用instance of進行類型判斷,然后直接通過(type)instance進行類型強轉(zhuǎn)甲献,將instance轉(zhuǎn)化為type類型的實例宰缤。如果instance不是type類型,那么將會拋出類型轉(zhuǎn)化異常。
而在kotlin中撵溃,可以通過as和is關鍵字進行類型判斷和轉(zhuǎn)化
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getItemViewType(position)) {
ITEM_TYPE_ACTIVITY -> {
if (holder is MemberAssignViewHolder) {
holder.bindData(activityList)
}
}
ITEM_TYPE_MEMBER_INFO_CARD -> {
if (holder is MemberInfoCardViewHolder) {
holder.bindData()
}
}
else -> {
holder.itemView.setPadding(0, 0, 0, if (position == itemCount - 1) UIUtil.dp2px(40f) else 0)
if (holder is BaseViewHolder) {
holder.holderType = BaseViewHolder.TYPE_MEMBER
}
super.onBindViewHolder(holder, position)
}
}
}
利用 a is type 來判斷a是否是type類型疚鲤,如果是type類型的話锥累,那么kotlin將會自動為我們將a轉(zhuǎn)化為type類型缘挑,而不需要我們再手動的利用as進行類型轉(zhuǎn)化。如果kotlin直接利用as進行類型轉(zhuǎn)化的話桶略,如果類型不一致或者a為null時语淘,也會拋出異常,但是kotlin提供了類型不一致時的安全轉(zhuǎn)化际歼,如下
val x: String ?= y as? String
上面的例子中惶翻,as?是一個安全轉(zhuǎn)化符,如果失敗鹅心,將會返回null吕粗,而不會直接進行類型轉(zhuǎn)化。
lambda
在java中旭愧,需要升級到java8才支持lambda表達颅筋,而kotlin天生支持lambda表達式,lambda表達式的完成語法如下:
val sum = { x: Int, y: Int -> x + y }
上面的例子定義了一個函數(shù)输枯,求得sum = x + y,lambda表達式總是括在大括號中议泵,完整的參數(shù)聲明和表達式都放在大括號內(nèi),函數(shù)體在->之后桃熄,->之前都是參數(shù)先口,并且可能有類型標注,如果推斷出該lambda的返回值類型不是Unit瞳收,也就是返回值不為空碉京,那么該lambda主體的最后一個表達式將被視為返回值
下面是某一個lambda的例子:
FindExchangeManager.getInstance().loadExchangeData(mContext, findInfo.discoveryModuleId,
holder.adapterPosition, findInfo, FindExchangeManager.ExchangeCallback { data, position, discoveryModuleId ->
if (Utility.isEmpty(mContext)) {
return@ExchangeCallback
}
val info = getMixInfoFromPosition(position)
if (data != null && info != null) {
info.topics = data
notifyItemChanged(position)
}
})
如果在lambda表達式中調(diào)用return,將會直接return掉整個上層函數(shù)螟深,因為lambda表達式只是一個代碼塊谐宙,雖然看起來功能像是函數(shù),不過也不能說在lambda表達式中不能夠return血崭,只要給return指定一個標簽卧惜,就會結束掉標簽所指定的代碼塊,就像上面的例子夹纫,這個lambda表達式是在ExchangeCallback中使用的咽瓷,如果要結束這個lambda,那么只需要在lambda中return代碼添加上@ExchangeCallback標簽就行舰讹,就繪結束掉ExchangeCallback的這個方法茅姜。
ViewHolder中使用kotlin extension
kotlin extension的使用需要先導入相應的xml
//在上一篇的activity中
import kotlinx.android.synthetic.main.activity_member_center.*
//在這個viewHolder中
import kotlinx.android.synthetic.main.listitem_assign.view.*
對于這個導包操作,需要注意的是,kotlinx.android.synthetic.main是固定前綴钻洒,后面的是當前頁面的layout文件名稱奋姿,如果是activity,只需要再在后面添加上.素标,那就能夠在activity中直接以id的形式調(diào)用對應的View称诗。如果是在viewHolder中,那么還需要添加上.view,在添加.才能夠使用头遭,而且在viewHolder中寓免,需要以其itemView為持有者來調(diào)用。
init {
context = itemView.context
itemView.btn_get_gift.setOnClickListener(this)
itemView.btn_not_gift.setOnClickListener(this)
}
如上所示计维,利用itemView來持有其中的view的id袜香,并直接調(diào)用。
這個init塊鲫惶,通常都是來做類的初始化的蜈首,在構造器執(zhí)行完之后,會調(diào)用這個init塊欠母,我們?nèi)绻惺裁葱枰龀跏蓟幕恫撸恍枰旁谶@個init塊中
String 轉(zhuǎn)其他類型
java中String轉(zhuǎn)long類型的話,需要調(diào)用Long.paras(string)進行轉(zhuǎn)化艺蝴,如果string不是相應的的類型猬腰,那么將會拋出異常。而在kotlin中也有這個方法猜敢,不過還新增了幾個轉(zhuǎn)化的方法:
//如果string不是一個合法的類型姑荷,那么將會拋出異常NumberFormatException
string.toLong()
//如果string不是一個合法的類型,那么將會返回null
string.toLongOrNull()
結合rxjava
kotlin中有專門的rx-kotlin庫缩擂,剛開始并沒有引入這個庫鼠冕,所以嘗試著還是使用rxjava
Observable.timer(getDelayTime(), TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
getGrabResult(activityId, thirdId)
})
還是一樣的用法,不過好的一點是可以使用lambda表達式了胯盯,subscribe({})中間的花括號就是lambda表達式
todo標注
在java中懈费,使用todo標識并不會出現(xiàn)什么問題,在kotlin中博脑,一旦我們實現(xiàn)了接口或者抽象類憎乙,那么我們自動生成的實現(xiàn)方法,將會帶上kotlin的todo標注
override fun refreshServerTime(time: Long?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
如果我們沒有刪除掉todo這行代碼叉趣,那么kotlin將會直接報錯泞边,kotlin.NotImplementedError,因為這個todo是kotlin默認我們將會刪除掉的疗杉,如果在運行時還存在阵谚,那么執(zhí)行到todo,并不會跳過,而是會執(zhí)行梢什,然后報錯奠蹬。
kotlin標注
在kotlin中,如果需要在kotlin的高階函數(shù)表達式返回嗡午,而不需要直接在外層函數(shù)中返回囤躁,或者嵌套循環(huán),想從內(nèi)層循環(huán)終止外層循環(huán)等翼馆,這種時候我們就需要用到kotlin的label了割以。比如說下面的例子:
private fun canAutoContinue(pos: Int): Boolean {
if (commonGoodList == null || continueGoodList == null || commonGoodList!!.size < pos) {
return false
}
val commonGood: RechargeGood? = commonGoodList!![pos]
continueGoodList!!.forEach {
if (it.upRenewId == commonGood?.id) {
return true
}
}
print("1111")
return false
}
上面的在forEach中金度,return的執(zhí)行將會直接將會結束掉canAutoContinue方法应媚,如果我們只想結束掉forEach,我們可以像下面這樣做猜极。
private fun canAutoContinue(pos: Int): Boolean {
if (commonGoodList == null || continueGoodList == null || commonGoodList!!.size < pos) {
return false
}
val commonGood: RechargeGood? = commonGoodList!![pos]
continueGoodList!!.forEach lit@{
if (it.upRenewId == commonGood?.id) {
return@lit
}
}
print("1111")
return false
}
如上面的lit label中姜,那么這樣return將會結束lit所指定的lable循環(huán),從而繼續(xù)往下執(zhí)行跟伏。
比如說for循環(huán)嵌套
outFor@ for(x in 1..5) {
for (y in 1..6) {
if(x == y) {
break@outFor
}
}
}
一旦滿足了x==y丢胚,然么將會直接跳出外層的for循環(huán),否則一般的break受扳,只會中斷內(nèi)層的for循環(huán)携龟。要為一個表達式加標簽,我們只要在其前加標簽即可勘高。
在返回和跳轉(zhuǎn)語句中峡蟋,可以指定標簽來表示結束哪個標簽對應的代碼段
如果需要在lambda表達式
kotlin 定義屬性時的get和set方法
kotlin定義屬性時,可以同時定義get和set方法华望,而不需要再重新定義方法去賦值蕊蝗。
private var delayTime: Long
private set(value) {
delayTime = value
}
get() {
return KKConfigManager.getInstance().
getConfig(KKConfigManager.ConfigType.GET_RECHARGE_ORDER_DELAY)
.toLongOrNull() ?: 1000
}
而且,可以添加上訪問限定符赖舟。而這個get和set方法的設置蓬戚,主要是通過代碼中的位置來標示修飾的哪個變量。
kotlin的幕后字段
Kotlin 中的類不能有字段宾抓, 然而我們在有時在自定義訪問器時子漩,也就是get和set方法時,需要又一個幕后字段石洗,這個字段是kotlin提供的幢泼,field,我們可以直接使用。
var counter = 0 // 注意:這個初始器直接為幕后字段賦值 set(value) {
if (value >= 0) field = value }
val nameHash:Int = 3
get() {
field = 5
return 10
}
有可能我們需要臨時緩存變量的值劲腿,因為可能其變量的值可能和返回的值不一致旭绒,所以一旦我們需要知道變量的值,我們便可以通過幕后字段來訪問。
kotlin的幕后屬性
幕后屬性主要用于外部只能讀挥吵,內(nèi)部可以讀寫的需求下出現(xiàn)的重父。例如下面的例子:
val counter get() = _counter
private var _counter = 0
這個_counter就是counter的幕后屬性,外部只能訪問counter忽匈,不能訪問_counter房午,而counter的值又是_counter來指定的。
kotlin的擴展
在kotlin中丹允,if else的三目運算符如下所示:
if(a) x else y
而我們也能通過擴展函數(shù)來擴展boolean
inline fun <T> Boolean.yes(action: () -> T): Boolean {
if (this) action()
return this
}
inline fun <T> Boolean.no(action: () -> T): Boolean {
if (!this) action()
return this
這樣郭厌,我們在kt文件中,只要引入相應的類雕蔽,定義在文件內(nèi)折柠,而非class下
import com.kuaikan.community.extend.yes
擴展函數(shù)是靜態(tài)解析的,所以不能夠進行重載批狐,這就意味著我們扇售,我們不能重載類原有的方法,比如說collection的add方法等..
對于擴展函數(shù)還有一個非常好用的東西嚣艇,被擴展的類型可以為空承冰,被稱為可空接收者
fun Any?.toString(): String {
if (this == null) return "null"
// 空檢測之后,“this”會自動轉(zhuǎn)換為非空類型食零,所以下面的 toString() // 解析為 Any 類的成員函數(shù)
return toString()
}
kotlin的擴展屬性
既然kotlin支持擴展函數(shù)困乒,當然也支持擴展屬性了。
val <T> List<T>.lastIndex: Int
get() = size - 1
這樣就給了List的一個屬性lastIndex
kotlin的范型
關于out和in這兩個關鍵字贰谣,out是用來輸出的娜搂,也就是生產(chǎn)者,所以只能作為返回類型冈爹,相當于java中的extends涌攻,用來界定類型上限;in是用來輸入的频伤,所以只能作為消費類型恳谎,in類似于java中的super,用來界定類型下限
kotlin的單例
在kotlin中憋肖,聲明單例變得非常簡單因痛,只需要一個關鍵字object用來修飾對象,就像變量聲明一樣岸更,對象聲明不是一個表達式鸵膏,不能用在賦值語句右邊,對象的初始化是線程安全的怎炊。如果需要引用該對象谭企,我們只需要通過其名稱來調(diào)用廓译。
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ......
}
val allDataProviders: Collection<DataProvider> get() = // ......
}
//調(diào)用
DataProviderManager.registerDataProvider(......)
伴生對象
類內(nèi)部的伴生對象的聲明如下:
class MyClass {
companion object { }
}
val x = MyClass.Companion
請注意,即使伴生對象的成員看起來像其他語言的靜態(tài)成員债查,在運行時他們?nèi)匀皇钦鎸崒ο蟮膶嵗蓡T非区,而且,例如還可以實現(xiàn)接口:
interface Factory<T> { fun create(): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass() }
}
如果我們想將伴生對象的成員生成為真正的靜態(tài)方法和字段盹廷,我們我們可以使用@JvmStatic注解
對象表達式和對象聲明之間的語義差異
對象表達式和對象聲明之間有一個重要的語義差別:
- 對象表達式是在使用他們的地方立即執(zhí)行(及初始化)的;
- 對象聲明是在第一次被訪問到時延遲初始化的;
- 伴生對象的初始化是在相應的類被加載(解析)時征绸,與 Java 靜態(tài)初始化器的語義相匹配。
onAttachedToWindow和onDetachedFromWindow
這個是無關kotlin的俄占,只是剛好在寫kt代碼時遇到的一個問題管怠,
關于onAttachedToWindow和onDetachedFromWindow的觸發(fā)時機,
onAttachedToWindow就是在這個View被添加到Window時觸發(fā)的
顧名思義缸榄,onAttachedToWindow就是在這個View被添加到Window時觸發(fā)的渤弛,通過了dispatchAttachedToWindow這個方法觸發(fā)的,那么View被添加到window的時機又是什么碰凶,其實就是對應的activity在生命周期onResume的時候調(diào)用的暮芭,activity對應的view在onResume的時候被添加添加到window。且每一個view都只會被調(diào)用一次欲低,父view調(diào)用在前,不論view的visibility狀態(tài)都會被調(diào)用畜晰,適合做些view特定的初始化操作砾莱;
onDetachedFromWindow
onDetachedFromWindow方法是在activity的生命周期destroy的時候被調(diào)用的,也就是act對應的view從window中移除的時候凄鼻,且每個view只會被調(diào)用一次腊瑟,父view的調(diào)用在后,也不論view的visibility狀態(tài)都會被調(diào)用块蚌,適合做最后的清理操作闰非;
kotlin類的繼承性
java的class默認都是可以繼承的,只有在聲明為final的class才是不可繼承的峭范。而kotlin卻是相反的财松,默認的類都是不可繼承的,也就是默認的修飾符為final纱控,只有顯式的聲明為open的類才是可以繼承的辆毡。而對于抽象類,java和kotlin默認都是可以繼承的甜害,但是子類必須是抽象類或者實現(xiàn)了該類的所有抽象方法舶掖。
kotlin擴展函數(shù)的java調(diào)用方式
kotlin中的擴展函數(shù)是非常方便的,剛開始以為在java中不能夠調(diào)用到kotlin中的擴展函數(shù)尔店,后面發(fā)現(xiàn)不是的眨攘,對于我們定義的擴展函數(shù)類主慰,在java中有相應的使用方式
//KotlinExt.kt
fun Int.dp2px(context: Context?): Int {
val scale = context?.resources?.displayMetrics?.density?.toInt()
return (this * (scale ?: 2) + 0.5f).toInt()
}
//java中調(diào)用
KotlinExtKt.dp2px(2, getContext());
我們只需要import相應的kotlinExt文件,然后就可以調(diào)用其中的方法鲫售,但使用方式和kotlin有不一致的地方河哑,對于kt的擴展函數(shù)的接收者,也就是上面的Int龟虎,在java中會被認為是第一個入?yún)ⅲ?如果擴展函數(shù)接受的是普通參數(shù)璃谨,那么這個參數(shù)就直接作為后續(xù)的入?yún)ⅰ?br> 如果擴展數(shù)據(jù)的第二個參數(shù)是lambda表達式,那么有java相應的生成方式:
//KotlinExt.kt
inline fun <T> Boolean.yes(action: () -> T): Boolean {
if (this) action()
return this
}
//java中調(diào)用
KotlinExtKt.yes(true, new Function0<Object>() {
@Override
public Object invoke() {
return null;
}
});
上面的Boolean的擴展yes方法鲤妥,接收一個高階函數(shù)佳吞,kt中用lambda表示,而在java中棉安,需要以一個匿名內(nèi)部類的方式替代底扳,
kotlin中內(nèi)部類
在java中,如果定義一個非靜態(tài)類內(nèi)部類贡耽,默認將會持有外部類的引用衷模,經(jīng)常會造成外部類無法被回收,導致內(nèi)存泄漏蒲赂,而kotlin中阱冶,內(nèi)部類默認不會持有外部類的引用,只有添加上inner關鍵字修飾滥嘴,才會持有外部引用木蹬。
kotlin 密封類
kotlin的密封類是java所沒有的,在kotlin中若皱,如果一個類被標明為密封類镊叁,那么其所有的子類都需要在父類中列出,作為密封類的嵌套內(nèi)部類
kotlin中的惰性集合操作
kotlin中一些集合操作走触,比如說map和filter晦譬,對于我們來說是非常方便的,比如說篩選一個人群的年齡大于30的人的名稱互广。
people.map(it.name).filter{it.age > 30}
但是上面這種方式敛腌,在鏈式調(diào)用的鏈足夠長時,將會產(chǎn)生性能問題兜辞,因為鏈式調(diào)用的每一個步驟都會創(chuàng)建一個中間集合迎瞧,用來存儲中間結果,也就是說逸吵,每一個步驟的結果都存儲在另外一個變量中凶硅。所以,kotlin給我們提供了另外一種使用方式扫皱,來避免創(chuàng)建中間對象足绅。也就是惰性集合捷绑。sequencez作為惰性集合操作,可以優(yōu)化map和filter等操作氢妈,將不會生成任何中間對象
people.asSequence().map(people.name).filter(it.startWith("a")).toList()
sequencez會將元數(shù)據(jù)生成一個流式對象粹污,對于集合的每一個元素利用iterator遍歷,執(zhí)行map和filter首量,篩選出符合條件的數(shù)據(jù)壮吩。
惰性集合的操作包含了中間操作和末端操作,中間操作就是轉(zhuǎn)換操作加缘,末端操作就是篩選操作鸭叙,在上面的例子里面,map是中間操作拣宏,filter是末端操作沈贝,如果沒有末端操作,那么勋乾,中間操作也不會執(zhí)行宋下,既然這樣,如果我們末端操作停止了辑莫,中間操作也會停止学歧,也就是說,并不會保證所有的中間操作都能夠執(zhí)行摆昧。
butterknife庫的使用
在java中撩满,butterknife庫是一個眾所周知的代替findViewId操作的庫,如果我們直接在kotlin中想要使用butterknife這個庫绅你,那還是需要一些額外的操作的。
引入KotterKnife或者自己提供extension
這個是butterknife作者提供的另外一個kotlin版本的替代庫昭躺。使用方式如下:
val updateTime by bind<TextView>(R.id.update_time)
雖然我沒看KotterKnife內(nèi)部實現(xiàn)忌锯,但是應該跟我們自己提供擴展函數(shù)的實現(xiàn)方式是一樣的,我們可以通過自己提供擴展函數(shù)來設置做這些操作:
fun <T : View> Activity.bind(@IdRes idRes: Int): Lazy<T> {
return lazy { findViewById<T>(idRes) }
}
fun <T : View> View.bind(@IdRes idRes: Int): Lazy<T> {
return lazy { findViewById<T>(idRes) }
}
fun <T : View> android.support.v7.widget.RecyclerView.ViewHolder.bind(@IdRes idRes: Int): Lazy<T> {
return lazy { this.itemView.findViewById<T>(idRes) }
}
fun <T : View> Fragment.bind(@IdRes idRes: Int): Lazy<T?> {
return lazy { this.view?.findViewById<T>(idRes) }
}
這個擴展數(shù)據(jù)其實就是代替我們執(zhí)行findViewById操作领炫,雖然我們是在初始化的時候定義了View的對象偶垮,但是它并不會馬上執(zhí)行,lazy關鍵字修飾的對象將會等到使用時再執(zhí)行相應方法帝洪。也就是說似舵,這個findViewById將會在setContentView之后執(zhí)行,因為我們使用這個對象肯定是在其之后的葱峡。
仍然使用butterknife
其實在kotlin中砚哗,我們還是可以使用butterknife的,只是我們需要將butterknife的注解解釋器替換為kotlin annotation Processor tool砰奕,這樣蛛芥,在kt文件中就能夠使用butterknife的注解了提鸟,我們不需要擔心會對之前的注解產(chǎn)生影響,因為kapt是兼容了annotationProcessor的仅淑。
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
====>
kapt 'com.jakewharton:butterknife-compiler:8.4.0'
替換了kapt之后称勋,就可以進行第二步了,在kt中利用laterinit修飾對象,來保證view對象可以在通過butterknife庫賦值
@BindView(R.id.user_v_layout)
lateinit var vLayout: ImageView
@BindView(R.id.comment_user_icon)
lateinit var userIconIV: ImageView
@BindView(R.id.comment_user_name)
lateinit var userNameTV: TextView