先來了解下集合初始化的方式
listOf創(chuàng)建的集合是一個(gè)<u>不可變的集合</u>,也就是不能調(diào)用添加或者刪除的方法。
//聲明一個(gè)list
val list= listOf("唐三","小舞","馬紅俊","戴沐白","朱竹清","奧斯卡","寧榮榮")
下面創(chuàng)建的集合是可變的。類似的set集合的創(chuàng)建方式是一樣的,set集合不允許重復(fù)元素。對(duì)應(yīng)的方式是setOf、mutableSetOf
val list= mutableListOf("唐三","小舞","馬紅俊","戴沐白","朱竹清","奧斯卡","寧榮榮");
list.add("白沉香")
map 的聲明和添加數(shù)據(jù)方式,以及遍歷數(shù)據(jù)。
val map=HashMap<String,Int>()
map["唐三"]=1;
map["小舞"]=2;
map["戴沐白"]=3;
for ((name,number) in map){
println("name is $name,number is $number")
}
也可以這樣定義初始化map集合
//這個(gè)方式定義的map同樣是不能修改的
val map = mapOf("唐三" to 1,"小舞" to 2,"戴沐白" to 3)
跟list一樣,如果需要修改map的數(shù)據(jù),像這樣定義map,就可以添加或者刪除數(shù)據(jù)
val map= mutableMapOf("唐三" to 1,"小舞" to 2,"戴沐白" to 3)
map["朱竹清"]=4
下面來看Lambda的使用
<u>Lambda 就是一小段可以作為參數(shù)傳遞的代碼</u>,這一小段代碼不宜過長(zhǎng),否則會(huì)影響可讀性。
<u>Lambda語法結(jié)構(gòu){參數(shù)名1:參數(shù)類型,參數(shù)名2:參數(shù)類型->函數(shù)體}</u>,最后一行代碼會(huì)自動(dòng)作為L(zhǎng)ambda表達(dá)式的返回值。
//獲取長(zhǎng)度最長(zhǎng)的人名
val list= mutableListOf("唐三" ,"小舞" ,"戴沐白" )
val maxLength=list.maxByOrNull { name : String -> name.length }
println("maxLength is $maxLength")
由于Kotlin出色的類型推到機(jī)制,Lambda表達(dá)式中參數(shù)的類型可以省略,上面的代碼可以簡(jiǎn)化
//由于出色的類型推到機(jī)制,上面的類型可以簡(jiǎn)化
val maxLength=list.maxByOrNull { name -> name.length }
當(dāng)Lambda表達(dá)式的參數(shù)列表只有一個(gè)的時(shí)候,也可以不必聲明參數(shù)名,而是可以使用it代替,進(jìn)一步簡(jiǎn)化
val maxLength=list.maxByOrNull { it.length }
上面的這種方式就是函數(shù)式API語法結(jié)構(gòu)
集合中的map函數(shù)是最常用的一種函數(shù)式API,它用于將集合中的每一個(gè)元素都映射成另外一個(gè)值,映射的規(guī)則在Lambda表達(dá)式中指定,最終生成一個(gè)新的集合
val list= listOf("tang san","xiao wu","dai mu bai","zhu zhu qing","ao si ka")
val newList=list.map { it.uppercase(Locale.CHINA) }
for (name in newList){
println("name is $name")
}
另外一個(gè)函數(shù)式API-filter,它是用來過濾集合中的數(shù)據(jù)的,它可以單獨(dú)使用也可以配合map一起使用。
val list= listOf("tang san","xiao wu","dai mu bai","zhu zhu qing","ao si ka")
val newList=list.filter { it.length>8 }.map { it.uppercase() }
for (name in newList){
println("name is $name")
}
先使用filter進(jìn)行條件篩選,然后再轉(zhuǎn)成大寫,順序可以調(diào)換。
函數(shù)值A(chǔ)PI--any 和 all,any表示是否存在,all表示是否全部是,這兩個(gè)是條件判斷,返回true and false
//函數(shù)值A(chǔ)PI any all
val list= listOf("tang san","xiao wu","dai mu bai","zhu zhu qing","ao si ka")
val any = list.any { it.length > 8 }
val all = list.all { it.length > 8 }
println("any is $any , all is $all") //any is true , all is false
java函數(shù)式API調(diào)用
如果在kotlin中調(diào)用一個(gè)java方法,并且該方法接收一個(gè)java單抽象方法接口參數(shù),就可以使用函數(shù)式API。
Thread { println("do some thing") }.start()
Thread 里邊需要一個(gè)參數(shù) Runnable ,Runnable里邊只有一個(gè)抽象run方法,所以可以直接省略這些。
val editText = EditText(context)
editText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun afterTextChanged(editable: Editable) {}
})
參數(shù)有多個(gè)抽象方法需要實(shí)現(xiàn),不能省略,書寫格式是這樣的。
空指針檢查
下面的函數(shù),當(dāng)調(diào)用的時(shí)候,是不能傳遞null參數(shù)的,直接會(huì)編譯不過去
fun readBooks(study: Study){
study.readBooks()
}
Kotlin利用編譯判空檢查機(jī)制幾乎杜絕了空指針的異常,如果業(yè)務(wù)邏輯需要傳null的場(chǎng)景,可以定義可以為null的參數(shù)類型
override fun onClick(v: View?) {
when (v?.id) {
R.id.iv_create_pci_finish -> picFinish()
}
}
這樣就可以傳null了,但是這樣寫依然編譯不過去,因?yàn)閭鬟f的參數(shù)可能會(huì)null就需要添加null判斷
Int? 可為null的整形 Int 不可為null的整形
String表示不可null的字符串 String?可為null的字符串
?.操作符
這個(gè)操作符表示不為null正常調(diào)用某個(gè)方法,當(dāng)對(duì)象為null則什么都不做。
?:操作符
這個(gè)操作符兩邊都接收一個(gè)表達(dá)式,如果左邊的表達(dá)式不為null就返回左邊的表達(dá)式,否則返回右邊的表達(dá)式
val c = a ?: b
這個(gè)表達(dá)式相當(dāng)于
val c = if (a != null) {
a
} else {
b
}
!!操作符
如果一個(gè)對(duì)象可能會(huì)null,這個(gè)時(shí)候這個(gè)對(duì)象調(diào)用某個(gè)方法編譯不會(huì)通過,如果不想添加判null操作,可以使用這個(gè)操作符!!,表示告訴kotlin,這個(gè)對(duì)象我非常確信不為null (感覺這個(gè)操作符沒啥用,直接加判null就好了)
//參數(shù)是一個(gè)可能會(huì)null的對(duì)象,這個(gè)時(shí)候如果不想添加判null需要編譯通過就需要使用!!這個(gè)操作符
mVideoFragment!!.seekTimeline(
timestamp,
NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_ANIMATED_STICKER_POSTER
)
let函數(shù),屬于kotlin的標(biāo)準(zhǔn)函數(shù)
let函數(shù)提供了函數(shù)式API的編程接口,并將原始調(diào)用對(duì)象作為參數(shù)傳遞到Lambda表達(dá)式中,let函數(shù)配合?.操作符非常好用
mTimelineEditor?.let {
it.setSequencLeftPadding(sequenceLeftPadding)
it.setSequencRightPadding(halfScreenWidth)
it.setTimeSpanLeftPadding(sequenceLeftPadding)
}
字符串的內(nèi)嵌表達(dá)式
之前打印數(shù)據(jù)的時(shí)候已經(jīng)涉及到了,比如之前的打印表達(dá)式
"any is $any , all is $all"
函數(shù)的參數(shù)默認(rèn)值
fun test(num:Int,name:String="Tom"){
println("num is $num, name is $name")
}
test(1) //這樣調(diào)用是合法的
這個(gè)函數(shù)第二個(gè)參數(shù)有一個(gè)默認(rèn)值,那么調(diào)用這個(gè)函數(shù)的時(shí)候,可以只傳遞第一個(gè)參數(shù),也可以兩個(gè)都傳遞
fun test(name:String="Tom",num:Int){
println("num is $num, name is $name")
}
test(num=1) //通過鍵值對(duì)的方式傳遞參數(shù)就可以調(diào)用了
如果第一個(gè)參數(shù)是帶默認(rèn)值,直接像test(1)就會(huì)報(bào)錯(cuò),kotlin認(rèn)為配型不匹配。這個(gè)問題可以使用鍵值對(duì)的方式進(jìn)行傳值,這個(gè)也是允許的
Kotlin 另外幾個(gè)常用的標(biāo)準(zhǔn)函數(shù)
with、run、aplly這幾個(gè)標(biāo)準(zhǔn)函數(shù)在開發(fā)中會(huì)經(jīng)常使用到,kotlin中的標(biāo)準(zhǔn)函數(shù)是指在Standard.kt文件中定義的函數(shù),任何Kotlin的代碼都可以隨便調(diào)用使用。
with函數(shù)接收兩個(gè)參數(shù),第一個(gè)參數(shù)是任意類型的對(duì)象,第二個(gè)參數(shù)是一個(gè)Lambda表達(dá)式,with函數(shù)會(huì)在Lambda表達(dá)式中提供第一個(gè)參數(shù)的對(duì)象的上下文,并使用Lambda表達(dá)式最后一行的表達(dá)式作為返回值。
NvsStreamingContext.SdkVersion sdkVersion = mStreamingContext.getSdkVersion();
StringBuilder stringBuilder = new StringBuilder("V ");
stringBuilder.append(sdkVersion.majorVersion);
stringBuilder.append(".");
stringBuilder.append(sdkVersion.minorVersion);
stringBuilder.append(".");
stringBuilder.append(sdkVersion.revisionNumber);
mSDKVersion.setText(stringBuilder.toString());
上面是sdk demo中的代碼,如果使用with函數(shù)進(jìn)行改造
NvsStreamingContext.SdkVersion sdkVersion = mStreamingContext.getSdkVersion();
val result=with(StringBuilder()){
append("V ")
append(sdkVersion.majorVersion)
append(".")
append(sdkVersion.minorVersion)
append(".")
append(sdkVersion.revisionNumber)
toString()
}
mSDKVersion.setText(result);
使用with函數(shù)代碼變的更加精簡(jiǎn)
run函數(shù)
這個(gè)函數(shù)跟with函數(shù)類似,得到的結(jié)果也相似。
val result=StringBuilder().run{
append("V ")
append(sdkVersion.majorVersion)
append(".")
append(sdkVersion.minorVersion)
append(".")
append(sdkVersion.revisionNumber)
toString()
}
mSDKVersion.setText(result);
aplly函數(shù)
這個(gè)函數(shù)跟run函數(shù)類型,但是最后一行不是返回值,而是返回對(duì)象本身,下面是一個(gè)實(shí)際應(yīng)用的例子。
mStartNextActivity.setOnClickListener {
val intent=Intent(this,OtherActivity::class.java).apply {
putExtra("param1",1)
putExtra("param2","2")
putExtra("param1",1)
}
startActivity(intent)
}
kotlin中定義靜態(tài)方法
相比于java,kotlin中因?yàn)閱卫芎?jiǎn)單,弱化靜態(tài)方法的概念。像java中的工具類Kotlin中推薦直接使用單例進(jìn)行實(shí)現(xiàn)。
object Utils {
private const val MIN_DELAY_TIME = 1000
private var lastClickTime: Long = 0
/**
*兩次點(diǎn)擊間隔不能少于1000ms
* The interval between two clicks cannot be less than 1000ms
*/
fun isFastClick(): Boolean {
var flag = true
val currentClickTime = System.currentTimeMillis()
if (currentClickTime - lastClickTime >= MIN_DELAY_TIME) {
flag = false
}
lastClickTime = currentClickTime
return flag
}
}
這樣處理之后,里邊所有的方法就可以像靜態(tài)方法那樣調(diào)用了。
如果某個(gè)類里邊,有對(duì)象方法也想要靜態(tài)方法怎么辦?可以使用companion object
class Utils {
companion object{
private val MIN_DELAY_TIME = 1000
private var lastClickTime: Long = 0
/**
*兩次點(diǎn)擊間隔不能少于1000ms
* The interval between two clicks cannot be less than 1000ms
*/
fun isFastClick(): Boolean {
var flag = true
val currentClickTime = System.currentTimeMillis()
if (currentClickTime - lastClickTime >= MIN_DELAY_TIME) {
flag = false
}
lastClickTime = currentClickTime
return flag
}
}
fun doAction(){
Log.e("Utils","doAction")
}
}
像上面這樣編寫就能解決上面的問題,isFastClick這個(gè)方法仍然可以像靜態(tài)方法那樣調(diào)用,doaction就是一個(gè)對(duì)象方法。但是這樣處理在原理上isFastClick并不是一個(gè)靜態(tài)方法,companion object這個(gè)關(guān)鍵字會(huì)在Utils類中創(chuàng)建伴生類,kotlin保證只會(huì)存在一個(gè)伴生類。
如果想要定義真正意義的靜態(tài)方法,kotlin提供了兩個(gè)方式:注解和頂層方法。
companion object只是在語法上模擬了靜態(tài)方法的調(diào)用方式,如果加上@JvmStatic注解,那么kotlin編譯器會(huì)將這個(gè)方法編譯成真正的靜態(tài)方法。
class Utils {
companion object{
private val MIN_DELAY_TIME = 1000
private var lastClickTime: Long = 0
/**
*兩次點(diǎn)擊間隔不能少于1000ms
* The interval between two clicks cannot be less than 1000ms
*/
@JvmStatic
fun isFastClick(): Boolean {
var flag = true
val currentClickTime = System.currentTimeMillis()
if (currentClickTime - lastClickTime >= MIN_DELAY_TIME) {
flag = false
}
lastClickTime = currentClickTime
return flag
}
}
fun doAction(){
Log.e("Utils","doAction")
}
}
這樣isFastClick就是真正意義上的靜態(tài)方法了。@JvmStatic 這個(gè)注解不能加在普通方法中會(huì)報(bào)錯(cuò)。
kotlin頂層方法
頂層方法是指,沒有定義在任何類中的方法。在kotlin中創(chuàng)建Kotlin file ,在file中定義的方法都是靜態(tài)方法,全局可以調(diào)用也是真正意義的靜態(tài)方法,全局可以調(diào)用。
延遲初始化
private var mTimeline: NvsTimeline? = null
這是聲明全局變量的方式,如果這樣初始化,那么在使用mTimeline這個(gè)對(duì)象的時(shí)候需要頻繁的使用?.進(jìn)行判空操作,否則編譯不過去,由于kotlin編譯機(jī)制就不得不編寫大量額外的判空操作。
如何解決這個(gè)問題呢?延時(shí)初始化lateinit使用這個(gè)關(guān)鍵字修飾全局變量,這樣聲明的變量不為null,使用的時(shí)候可以避免頻繁的判空操作。
private lateinit var mTimeline: NvsTimeline
但是這個(gè)關(guān)鍵字本身是有風(fēng)險(xiǎn)的,如果使用之前沒有初始化會(huì)拋異常,所以當(dāng)對(duì)一個(gè)全局的變量使用了lateinit關(guān)鍵字,請(qǐng)確保它在被任何地方調(diào)用之前已經(jīng)初始化。
在使用全局變量之前可以通過代碼來判斷全部變量是否已經(jīng)初始化,::mTimeline.isInitialized這個(gè)就是判斷對(duì)象是否會(huì)null的方法。
if(!::mTimeline.isInitialized){
mTimeline=initTimeline()
}
擴(kuò)展函數(shù)
擴(kuò)展函數(shù)表示在不修改某個(gè)類源碼的基礎(chǔ)上,仍然可以打開這個(gè)類,向該類添加新方法。
擴(kuò)展函數(shù)的語法結(jié)構(gòu)
fun ClassName.methodName(param1:Int,Param2:Int):Int{
return 0;
}
擴(kuò)展函數(shù)可以放在頂層函數(shù),這樣擴(kuò)展函數(shù)就擁有了全局的訪問域。
fun String.lowersCount():Int{
var count=0
for (low in this){
if (low.isLowerCase()){
count++
}
}
return count
}
//測(cè)試方法
val lowNum="asdWERff".lowersCount()
println("lowNum is $lowNum ")
//輸出日志
//lowNum is 5
上面是給String定義的一個(gè)統(tǒng)計(jì)小寫字母?jìng)€(gè)數(shù)的方法,這個(gè)在String類里邊是沒有的。利用這個(gè)特性,可以寫出豐富多樣的擴(kuò)展函數(shù),非常好用。擴(kuò)展函數(shù)在kotlin中沒有任何限制,可以再任何類上添加擴(kuò)展函數(shù),這將大大提升代碼質(zhì)量和研發(fā)效率。