先來了解下集合初始化的方式
listOf創(chuàng)建的集合是一個<u>不可變的集合</u>策菜,也就是不能調用添加或者刪除的方法具钥。
//聲明一個list
val list= listOf("唐三","小舞","馬紅俊","戴沐白","朱竹清","奧斯卡","寧榮榮")
下面創(chuàng)建的集合是可變的微服。類似的set集合的創(chuàng)建方式是一樣的葡兑,set集合不允許重復元素镀娶。對應的方式是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集合
//這個方式定義的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>儿奶,這一小段代碼不宜過長,否則會影響可讀性鳄抒。
<u>Lambda語法結構{參數(shù)名1:參數(shù)類型闯捎,參數(shù)名2:參數(shù)類型->函數(shù)體}</u>,最后一行代碼會自動作為Lambda表達式的返回值许溅。
//獲取長度最長的人名
val list= mutableListOf("唐三" ,"小舞" ,"戴沐白" )
val maxLength=list.maxByOrNull { name : String -> name.length }
println("maxLength is $maxLength")
由于Kotlin出色的類型推到機制瓤鼻,Lambda表達式中參數(shù)的類型可以省略,上面的代碼可以簡化
//由于出色的類型推到機制贤重,上面的類型可以簡化
val maxLength=list.maxByOrNull { name -> name.length }
當Lambda表達式的參數(shù)列表只有一個的時候茬祷,也可以不必聲明參數(shù)名,而是可以使用it代替并蝗,進一步簡化
val maxLength=list.maxByOrNull { it.length }
上面的這種方式就是函數(shù)式API語法結構
集合中的map函數(shù)是最常用的一種函數(shù)式API祭犯,它用于將集合中的每一個元素都映射成另外一個值,映射的規(guī)則在Lambda表達式中指定滚停,最終生成一個新的集合
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")
}
另外一個函數(shù)式API-filter沃粗,它是用來過濾集合中的數(shù)據(jù)的,它可以單獨使用也可以配合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進行條件篩選最盅,然后再轉成大寫,順序可以調換镰吵。
函數(shù)值API--any 和 all檩禾,any表示是否存在,all表示是否全部是疤祭,這兩個是條件判斷盼产,返回true and false
//函數(shù)值API 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調用
如果在kotlin中調用一個java方法,并且該方法接收一個java單抽象方法接口參數(shù)勺馆,就可以使用函數(shù)式API戏售。
Thread { println("do some thing") }.start()
Thread 里邊需要一個參數(shù) Runnable 侨核,Runnable里邊只有一個抽象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ù)有多個抽象方法需要實現(xiàn)搓译,不能省略,書寫格式是這樣的锋喜。
空指針檢查
下面的函數(shù)些己,當調用的時候,是不能傳遞null參數(shù)的嘿般,直接會編譯不過去
fun readBooks(study: Study){
study.readBooks()
}
Kotlin利用編譯判空檢查機制幾乎杜絕了空指針的異常段标,如果業(yè)務邏輯需要傳null的場景,可以定義可以為null的參數(shù)類型
override fun onClick(v: View?) {
when (v?.id) {
R.id.iv_create_pci_finish -> picFinish()
}
}
這樣就可以傳null了炉奴,但是這樣寫依然編譯不過去逼庞,因為傳遞的參數(shù)可能會null就需要添加null判斷
Int? 可為null的整形 Int 不可為null的整形
String表示不可null的字符串 String?可為null的字符串
瞻赶?.操作符
這個操作符表示不為null正常調用某個方法赛糟,當對象為null則什么都不做。
?:操作符
這個操作符兩邊都接收一個表達式砸逊,如果左邊的表達式不為null就返回左邊的表達式璧南,否則返回右邊的表達式
val c = a ?: b
這個表達式相當于
val c = if (a != null) {
a
} else {
b
}
!!操作符
如果一個對象可能會null,這個時候這個對象調用某個方法編譯不會通過痹兜,如果不想添加判null操作穆咐,可以使用這個操作符!!,表示告訴kotlin字旭,這個對象我非常確信不為null (感覺這個操作符沒啥用,直接加判null就好了)
//參數(shù)是一個可能會null的對象,這個時候如果不想添加判null需要編譯通過就需要使用!!這個操作符
mVideoFragment!!.seekTimeline(
timestamp,
NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_ANIMATED_STICKER_POSTER
)
let函數(shù)崖叫,屬于kotlin的標準函數(shù)
let函數(shù)提供了函數(shù)式API的編程接口遗淳,并將原始調用對象作為參數(shù)傳遞到Lambda表達式中,let函數(shù)配合心傀?.操作符非常好用
mTimelineEditor?.let {
it.setSequencLeftPadding(sequenceLeftPadding)
it.setSequencRightPadding(halfScreenWidth)
it.setTimeSpanLeftPadding(sequenceLeftPadding)
}
字符串的內嵌表達式
之前打印數(shù)據(jù)的時候已經涉及到了屈暗,比如之前的打印表達式
"any is $any , all is $all"
函數(shù)的參數(shù)默認值
fun test(num:Int,name:String="Tom"){
println("num is $num, name is $name")
}
test(1) //這樣調用是合法的
這個函數(shù)第二個參數(shù)有一個默認值,那么調用這個函數(shù)的時候脂男,可以只傳遞第一個參數(shù)养叛,也可以兩個都傳遞
fun test(name:String="Tom",num:Int){
println("num is $num, name is $name")
}
test(num=1) //通過鍵值對的方式傳遞參數(shù)就可以調用了
如果第一個參數(shù)是帶默認值,直接像test(1)就會報錯宰翅,kotlin認為配型不匹配弃甥。這個問題可以使用鍵值對的方式進行傳值,這個也是允許的
Kotlin 另外幾個常用的標準函數(shù)
with汁讼、run淆攻、aplly這幾個標準函數(shù)在開發(fā)中會經常使用到阔墩,kotlin中的標準函數(shù)是指在Standard.kt文件中定義的函數(shù),任何Kotlin的代碼都可以隨便調用使用瓶珊。
with函數(shù)接收兩個參數(shù)啸箫,第一個參數(shù)是任意類型的對象,第二個參數(shù)是一個Lambda表達式伞芹,with函數(shù)會在Lambda表達式中提供第一個參數(shù)的對象的上下文忘苛,并使用Lambda表達式最后一行的表達式作為返回值。
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ù)進行改造
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ù)代碼變的更加精簡
run函數(shù)
這個函數(shù)跟with函數(shù)類似扎唾,得到的結果也相似。
val result=StringBuilder().run{
append("V ")
append(sdkVersion.majorVersion)
append(".")
append(sdkVersion.minorVersion)
append(".")
append(sdkVersion.revisionNumber)
toString()
}
mSDKVersion.setText(result);
aplly函數(shù)
這個函數(shù)跟run函數(shù)類型绊汹,但是最后一行不是返回值稽屏,而是返回對象本身,下面是一個實際應用的例子。
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中因為單例很簡單狐榔,弱化靜態(tài)方法的概念。像java中的工具類Kotlin中推薦直接使用單例進行實現(xiàn)获雕。
object Utils {
private const val MIN_DELAY_TIME = 1000
private var lastClickTime: Long = 0
/**
*兩次點擊間隔不能少于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)方法那樣調用了。
如果某個類里邊届案,有對象方法也想要靜態(tài)方法怎么辦庵楷?可以使用companion object
class Utils {
companion object{
private val MIN_DELAY_TIME = 1000
private var lastClickTime: Long = 0
/**
*兩次點擊間隔不能少于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這個方法仍然可以像靜態(tài)方法那樣調用楣颠,doaction就是一個對象方法尽纽。但是這樣處理在原理上isFastClick并不是一個靜態(tài)方法,companion object這個關鍵字會在Utils類中創(chuàng)建伴生類童漩,kotlin保證只會存在一個伴生類弄贿。
如果想要定義真正意義的靜態(tài)方法,kotlin提供了兩個方式:注解和頂層方法矫膨。
companion object只是在語法上模擬了靜態(tài)方法的調用方式差凹,如果加上@JvmStatic注解,那么kotlin編譯器會將這個方法編譯成真正的靜態(tài)方法侧馅。
class Utils {
companion object{
private val MIN_DELAY_TIME = 1000
private var lastClickTime: Long = 0
/**
*兩次點擊間隔不能少于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 這個注解不能加在普通方法中會報錯。
kotlin頂層方法
頂層方法是指馁痴,沒有定義在任何類中的方法谊娇。在kotlin中創(chuàng)建Kotlin file ,在file中定義的方法都是靜態(tài)方法弥搞,全局可以調用也是真正意義的靜態(tài)方法邮绿,全局可以調用航攒。
延遲初始化
private var mTimeline: NvsTimeline? = null
這是聲明全局變量的方式炕柔,如果這樣初始化羡亩,那么在使用mTimeline這個對象的時候需要頻繁的使用唱逢?.進行判空操作,否則編譯不過去挖胃,由于kotlin編譯機制就不得不編寫大量額外的判空操作杂靶。
如何解決這個問題呢?延時初始化lateinit使用這個關鍵字修飾全局變量酱鸭,這樣聲明的變量不為null吗垮,使用的時候可以避免頻繁的判空操作。
private lateinit var mTimeline: NvsTimeline
但是這個關鍵字本身是有風險的凹髓,如果使用之前沒有初始化會拋異常烁登,所以當對一個全局的變量使用了lateinit關鍵字,請確保它在被任何地方調用之前已經初始化蔚舀。
在使用全局變量之前可以通過代碼來判斷全部變量是否已經初始化饵沧,::mTimeline.isInitialized這個就是判斷對象是否會null的方法。
if(!::mTimeline.isInitialized){
mTimeline=initTimeline()
}
擴展函數(shù)
擴展函數(shù)表示在不修改某個類源碼的基礎上赌躺,仍然可以打開這個類狼牺,向該類添加新方法。
擴展函數(shù)的語法結構
fun ClassName.methodName(param1:Int,Param2:Int):Int{
return 0;
}
擴展函數(shù)可以放在頂層函數(shù)礼患,這樣擴展函數(shù)就擁有了全局的訪問域是钥。
fun String.lowersCount():Int{
var count=0
for (low in this){
if (low.isLowerCase()){
count++
}
}
return count
}
//測試方法
val lowNum="asdWERff".lowersCount()
println("lowNum is $lowNum ")
//輸出日志
//lowNum is 5
上面是給String定義的一個統(tǒng)計小寫字母個數(shù)的方法,這個在String類里邊是沒有的缅叠。利用這個特性悄泥,可以寫出豐富多樣的擴展函數(shù),非常好用肤粱。擴展函數(shù)在kotlin中沒有任何限制码泞,可以再任何類上添加擴展函數(shù),這將大大提升代碼質量和研發(fā)效率狼犯。