前言
部門經理讓我在技術分享會上分享Kotlin的語法,因爲Kotlin已經被Google定爲一級開發語言了,正好之前也有學習Kotlin的計劃於是花2天時間熟悉了下Kotlin的語法以及特性。
語法
關於語法的介紹比較多,這裏就貼一些典型的例子
- 變量的聲明
var a : Int?=null //?表示該變量可爲null
var b : Int = 2 // 不加?表示該變量不爲null
var c = "Hello World" //類型自推導
在Kotlin中聲明變量是用標識符var ,常量用val
格式是
var 變量名 :類型?= .... //?表示該變量可爲null,不加?表示該變量不爲null
- 類型轉換
var e : Long= b.toLong() //通過toLong(),toByte()等方法進行類型轉換
var f : Int= "1".toInt() //將字符串"1"變爲一個Int
- 字符串拼接
var name:String?="Bryant"
var anotherName:String?= "Hello $name"
- 函數的定義
fun 關鍵字聲明 具體形式 fun 函數名(變量名1 : 類型,變量名2 : 類型……):返回值類型
fun sum(a:Int,b:Int):Int = a+b //當函數返回單個表達式時,可以省略花括號並且在 = 符號之後指定代碼體即可寫成單表達式函數
//表達式有多個使用大括號,when的作用類似於Java中的Switch
fun typeCheck(obj:Any):Int?{
when(obj){
!is Int ->return null
else -> return obj
}
}
Kotlin還支持給函數參數默認值
fun init(a : Int = 1 ,b : Int = 2) : Int = a+b //如果不給a,b賦值,那麼就使用默認值
- for循環的使用
遍歷items
val items = listOf<String>("apple","banana","orange","juice")
for(i in items.indices){
println("item: ${items[i]}") //根據索引輸入item
}
或者這種形式
for(i in items){
println("item: $i") //直接輸出item
}
- !! 和 ?.的含義
拋出NullPointerException異常使用 !!
var a : String?=null
println(a!!.length)
安全調用操作符寫作 ?. 使用這個符號會得到null的輸出,還是這個例子,我們會打印null
var a : String?=null
println(a?.length)
Kotlin 的類型系統旨在從我們的代碼中消除 NullPointerException。NPE 可能的原因是
顯式調用 throw NullPointerException()
使用了 !! 操作符
外部 Java 代碼導致的
對於初始化,有一些數據不一致(如一個未初始化的 this 用於構造函數的某個地方)
安全的類型轉換
如果對象不是目標類型,那麼常規類型轉換可能會導致 ClassCastException。 另一個選擇是使用安全的類型轉換,如果嘗試轉換不成功則返回 null:
val aInt: Int? = a as? Int
- 冒號 (:) 的作用
變量(常量)的聲明 ,參數的聲明,實現接口,方法返回值的聲明 等作用
基礎的語法大概就是這麼多,接下來介紹一個比較推薦的東西——協程
協程
- 協程是什麼?
一些 API 啓動長時間運行的操作(例如網絡 IO、文件 IO、CPU 或 GPU 密集型任務等),並要求調用者阻塞直到它們完成。協程提供了一種避免阻塞線程並用更廉價、更可控的操作替代線程阻塞的方法:協程掛起。
協程通過將複雜性異步放入庫來簡化異步編程。程序的邏輯可以在協程中順序地表達,而底層庫會爲我們解決其異步性。該庫可以將用戶代碼的相關部分包裝爲回調、訂閱相關事件、在不同線程(甚至不同機器!)上調度執行,而代碼則保持如同順序執行一樣簡單。 - 協程的具體實現
直接上代碼
我們先進行一個簡單的異步輸出操作
fun main(args: Array<String>) = runBlocking<Unit>{ //創建主協程
var job = launch(CommonPool){
delay(1000)
println("我是子協程,我延遲了一秒")
}
println("我是主協程,我先執行")
delay(2000)
println("我是主協程,我執行完了")
}
我們看一下結果
這裏的delay方法是一個掛起方法(suspend),它只能在協程中被調用,因爲我們在main方法後面加了
fun main(args: Array<String>) = runBlocking<Unit>
因爲main方法現在是一個協程,並且是主協程
那麼這串代碼就好理解了,我們開始先定義一個協程的任務,然後順序執行,子協程運行的同時,主協程還在運行,在這個例子中子協程比主協程先運行完,所以如圖上輸出結果,但是還有一種情況是子協程需要運行的時間比主協程的長
fun main(args: Array<String>) = runBlocking{ //創建主協程
var job = launch(CommonPool){
delay(3000)
println("我是子協程,我延遲了3秒")
}
println("我是主協程,我先執行")
delay(1000)
println("我是主協程,我執行完了")
}
結果:
我們發現子協程裏面的輸出不見了,由此可見,子協程的執行時間是要小於主協程的,但有時我們又不知道子協程運行了多長時間怎麼辦,我們採用join方法
fun main(args: Array<String>) = runBlocking{ //創建主協程
var job = launch(CommonPool){
delay(3000)
println("我是子協程,我延遲了3秒")
}
println("我是主協程,我先執行")
job.join()
println("我是主協程,我執行完了")
}
這樣我們就不用計算子協程需要運行的時間,但是如果我們想要取消子協程的任務怎麼辦,也很簡單,只需要在主協程中加一條job.cancel()就能取消子協程
fun main(args: Array<String>) = runBlocking{ //創建主協程
var job = launch(CommonPool){
repeat(1000){
i ->
println("我是子協程,我重複執行了 $i 次")
delay(500)
}
}
println("我是主協程,我先執行")
delay(2000)
println("我是主協程,我等不及了,你給我取消")
job.cancel()
println("我是主協程,我執行完了")
}
結果:
但是cancel()並不是所有協程任務都能取消,比如下面這種情況就不能取消
fun main(args: Array<String>) = runBlocking{ //創建主協程
var job = launch(CommonPool){
var nextPrintTime = 0L
var i = 0
while (i < 10) { // 循環運算
val currentTime = System.currentTimeMillis()
if (currentTime >= nextPrintTime) {
println("我是子協程,不能被取消,執行了 ${i++} 次")
nextPrintTime = currentTime + 500L
}
}
}
println("我是主協程,我先執行")
delay(1300L)
println("我是主協程,我等不及了,你給我取消")
job.cancel()
delay(1300L)
println("我是主協程,我執行完了")
}
結果:
發現如果在進行循環操作的協程不能被取消,我們接下來取消循環運算協程
fun main(args: Array<String>) = runBlocking{ //創建主協程
var job = launch(CommonPool){
var nextPrintTime = 0L
var i = 0
while (isActive) { // i<10改爲 isActive 即可
val currentTime = System.currentTimeMillis()
if (currentTime >= nextPrintTime) {
println("我是子協程,不能被取消,執行了 ${i++} 次")
nextPrintTime = currentTime + 500L
}
}
}
println("我是主協程,我先執行")
delay(1300L)
println("我是主協程,我等不及了,你給我取消")
job.cancel()
delay(1300L)
println("我是主協程,我執行完了")
}
結果:
關於協程的內容就介紹到這裏,其他的以後再補充