学习Kotlin(八)其他技术
?
?
推薦閱讀:
學習Kotlin(一)為什么使用Kotlin
學習Kotlin(二)基本語法
學習Kotlin(三)類和接口
學習Kotlin(四)對象與泛型
學習Kotlin(五)函數與Lambda表達式
學習Kotlin(六)擴展與委托
學習Kotlin(七)反射和注解
學習Kotlin(八)其他技術
Kotlin學習資料總匯
?
目錄
一、解構聲明
二、區間
三、類型檢查與轉換
四、this表達式
五、相等性
六、操作符重載
七、空安全
八、異常
九、類型別名
一、解構聲明
解構聲明能同時創建多個變量,將對象中的數據解析成相對的變量。舉個例子:
//創建一個數據類User data class User(var name: String, var age: Int)//獲得User的實例 var user = User("Czh", 22) //聲明變量 name 和 age var (name, age) = userprintln("name:$name age:$age") //輸出結果為:name:Czh age:22上面代碼中用解構聲明同時創建兩個變量的時候,會被編譯成以下代碼:
//指定變量name的值為user第一個參數的值 var name = user.component1() //指定變量name的值為user第二個參數的值 var age = user.component2()println("name:$name age:$age") //輸出結果為:name:Czh age:22- 解構聲明和Map
Map可以保存一組key-value鍵值對,通過解構聲明可以把這些值解構出來。如下所示:
運行代碼,輸出結果:
二、區間
1.in
假如現在要判斷 i 是否在 1-5 內,可以這樣寫:
if (i in 1..5) {println("i 在 1-5 內") }上面代碼中,1..5指的是 1-5,in指的是在...范圍內,如果 i 在范圍 1-5 之內,將會執行后面的代碼塊,輸出結果。如果想判斷 i 是否不在 1-5 內,可以這樣寫:
//!in表示不在...范圍內 if (i !in 1..5) {println("i 不在 1-5 內") }上面兩段代碼等同于:
if (i >= 1 && i <= 5) {println("i 在 1-5 內") } if (i <= 1 && i >= 5) {println("i 不在 1-5 內") }2.downTo
如果想輸出 1-5 ,可以這樣寫:
for (i in 1..5) println(i) //輸出12345如果倒著來:
for (i in 5..1) println(i) //什么也不輸出這個時候可以用downTo函數倒序輸出 5-1
for (i in 5 downTo 1) println(i)3.step
上面的代碼順序輸出12345或倒序54321,按順序+1或者-1,也就是步長為1。如果要修改步長,可以用step函數,如下所示:
for (i in 1..5 step 2) println(i) //輸出135//倒序 for (i in 1 downTo 5 step 2) println(i) //輸出5314.until
上面的代碼中,使用的范圍都是閉區間,例如1..5的區間是[1,5],如果要創建一個不包括其結束元素的區間,即區間是[1,5),可以使用until函數,如下所示:
for (i in 1 until 5) println(i) //輸出1234三、類型檢查與轉換
1.is操作符
在Kotlin中,可以通過is操作符判斷一個對象與指定的類型是否一致,還可以使用is操作符的否定形式!is,舉個例子:
var a: Any = "a" if (a is String) {println("a是String類型") } if (a !is Int) {println("a不是Int類型") }運行代碼,輸出結果為:
2.智能轉換
在Kotlin中不必使用顯式類型轉換操作,因為編譯器會跟蹤不可變值的is檢查以及顯式轉換,并在需要時自動插入(安全的)轉換。舉個例子:
var a: Any = "a" if (a is String) {println("a是String類型")println(a.length) // a 自動轉換為String類型//輸出結果為:1 }還可以反向檢查,如下所示:
if (a !is String) return print(a.length) // a 自動轉換為String類型在 && 和 || 的右側也可以智能轉換:
// `&&` 右側的 a 自動轉換為String if (a is String && a.length > 0)// `||` 右側的 a 自動轉換為String if (a !is String || a.length > 0)在when表達式和while循環里也能智能轉換:
when(a){is String -> a.lengthis Int -> a + 1 }需要注意的是,當編譯器不能保證變量在檢查和使用之間不可改變時,智能轉換不能用。智能轉換能否適用根據以下規則:
- val?局部變量——總是可以,局部委托屬性除外;
- val?屬性——如果屬性是 private 或 internal,或者該檢查在聲明屬性的同一模塊中執行。智能轉換不適用于 open 的屬性或者具有自定義 getter 的屬性;
- var?局部變量——如果變量在檢查和使用之間沒有修改、沒有在會修改它的 lambda 中捕獲、并且不是局部委托屬性;
- var?屬性——決不可能(因為該變量可以隨時被其他代碼修改)
3.強制類型轉換
在Kotlin中,用操作符as進行強制類型轉換,如下所示:
var any: Any = "abc" var str: String = any as String但強制類型轉換是不安全的,如果類型不兼容,會拋出一個異常,如下所示:
var int: Int = 123 var str: String = int as String //拋出ClassCastException4.可空轉換操作符
null不能轉換為?String,因該類型不是可空的。舉個例子:
var str = null var str2 = str as String //拋出TypeCastException解決這個問題可以使用可空轉換操作符as?,如下所示:
var str = null var str2 = str as? String println(str2) //輸出結果為:null使用安全轉換操作符as?可以在轉換失敗時返回null,避免了拋出異常。
四、this表達式
為了表示當前的接收者我們使用this表達式。當this在類的成員中,this指的是該類的當前對象;當this在擴展函數或者帶接收者的函數字面值中,this表示在點左側傳遞的接收者參數。
- 限定的this如果this沒有限定符,它指的是最內層的包含它的作用域。如果要訪問來自外部作用域的this(一個類或者擴展函數, 或者帶標簽的帶接收者的函數字面值)我們使用this@label,其中?@label?是一個代指this來源的標簽。舉個例子:
五、相等性
在Kotlin中存在結構相等和引用相等兩中相等判斷。
1.結構相等
使用equals()或==判斷,如下所示:
var a = "1" var b = "1" if (a.equals(b)) {println("a 和 b 結構相等")//輸出結果為:a 和 b 結構相等 }var a = 1 var b = 1 if (a == b) {println("a 和 b 結構相等")//輸出結果為:a 和 b 結構相等 }2.引用相等
引用相等指兩個引用指向同一對象,用===判斷,如下所示:
data class User(var name: String, var age: Int)var a = User("Czh", 22) var b = User("Czh", 22) var c = b var d = a if (c == d) {println("a 和 b 結構相等") } else {println("a 和 b 結構不相等") } if (c === d) {println("a 和 b 引用相等") } else {println("a 和 b 引用不相等") }運行代碼,輸出結果為:
六、操作符重載
Kotlin允許對自己的類型提供預定義的一組操作符的實現,這些操作符具有固定的符號表示 (如?+?或?*)和固定的優先級。為實現這樣的操作符,我們為相應的類型(即二元操作符左側的類型和一元操作符的參數類型)提供了一個固定名字的成員函數或擴展函數。 重載操作符的函數需要用?operator?修飾符標記。
重載操作符
+是一個一元操作符,下面來對一元操作符進行重載:
//用 operator 修飾符標記 operator fun String.unaryPlus(): String {return this + this }//調用 var a = "a" println(+a) //輸出結果為:aa當編譯器處理例如表達式 +a 時,它執行以下步驟:
- 確定 a 的類型,令其為 T;
- 為接收者 T 查找一個帶有 operator 修飾符的無參函數 unaryPlus(),即成員函數或擴展函數;
- 如果函數不存在或不明確,則導致編譯錯誤;
- 如果函數存在且其返回類型為 R,那就表達式 +a 具有類型 R;
除對一元操作符進行重載外,還可以對其他操作符進行重載,其重載方式和原理大致相同。下面來一一列舉:
1.一元操作符
| +a | a.unaryPlus() |
| -a | a.unaryMinus() |
| !a | a.not() |
| a++ | a.inc() |
| a-- | a.dec() |
2.二元操作符
| a+b | a.plus(b) |
| a-b | a.minus(b) |
| a*b | a.times(b) |
| a/b | a.div(b) |
| a%b | a.mod(b) |
| a..b | a.rangeTo(b) |
3.in操作符
| a in b | b.contains(a) |
| a !in b | !b.contains(a) |
4.索引訪問操作符
| a[i] | a.get(i) |
| a[i, j] | a.get(i, j) |
| a[i_1, ……, i_n] | a.get(i_1, ……, i_n) |
| a[i] = b | a.set(i, b) |
| a[i, j] = b | a.set(i, j, b) |
| a[i_1, ……, i_n] = b | a.set(i_1, ……, i_n, b) |
5.調用操作符
| a() | a.invoke() |
| a(i) | a.invoke(i) |
| a(i, j) | a.invoke(i, j) |
| a(i_1, ……, i_n) | a.invoke(i_1, ……, i_n) |
6.廣義賦值
| a += b | a.plusAssign(b) |
| a -= b | a.minusAssign(b) |
| a *= b | a.timesAssign(b) |
| a /= b | a.divAssign(b) |
| a %= b | a.remAssign(b), a.modAssign(b)(已棄用) |
7.相等與不等操作符
| a == b | a?.equals(b) ?: (b === null) |
| a != b | !(a?.equals(b) ?: (b === null)) |
8.比較操作符
| a > b | a.compareTo(b) > 0 |
| a < b | a.compareTo(b) < 0 |
| a >= b | a.compareTo(b) >= 0 |
| a <= b | a.compareTo(b) <= 0 |
七、空安全
在Java中,NullPointerException 可能是最常見的異常之一,而Kotlin的類型系統旨在消除來自代碼空引用的危險。
1.可空類型與非空類型
在Kotlin中,只有下列情況可能導致出現NullPointerException:
- 顯式調用 throw NullPointerException();
- 使用了下文描述的 !! 操作符;
- 有些數據在初始化時不一致;
- 外部 Java 代碼引發的問題。
在 Kotlin 中,類型系統區分一個引用可以容納 null (可空引用)還是不能容納(非空引用)。 例如,String 類型的常規變量不能容納 null:
如果要允許為空,我們可以聲明一個變量為可空字符串,在字符串類型后面加一個問號?,寫作 String?,如下所示:
?
var b: String? = "b" b = null2.安全調用操作符
接著上面的代碼,如果你調用a的方法或者訪問它的屬性,不會出現NullPointerException,但如果調用b的方法或者訪問它的屬性,編譯器會報告一個錯誤,如下所示:
這個時候可以使用安全調用操作符,寫作?.,在b后面加安全調用操作符,表示如果b不為null則調用b.length,如下所示:
?
b?.length安全調用操作符還能鏈式調用,例如一個員工 Bob 可能會(或者不會)分配給一個部門, 并且可能有另外一個員工是該部門的負責人,那么獲取 Bob 所在部門負責人(如果有的話)的名字,我們寫作:
Bob?.department?.head?.name //如果Bob分配給一個部門 //執行Bob.department.head?獲取該部門的負責人 //如果該部門有一個負責人 //執行Bob.department.head.name獲取該負責人的名字如果該鏈式調用中任何一個屬性為null,整個表達式都會返回null。如果要只對非空值執行某個操作,安全調用操作符可以與let一起使用,如下所示:
val listWithNulls: List<String?> = listOf("A", null, "B") for (item in listWithNulls) {item?.let { println(it) } }運行代碼,輸出結果為:
- 安全的類型轉換
如果對象不是目標類型,那么常規類型轉換可能會導致?ClassCastException。 另一個選擇是使用安全的類型轉換,如果嘗試轉換不成功則返回null,如下所示:
- 可空類型的集合
如果你有一個可空類型元素的集合,并且想要過濾非空元素,你可以使用filterNotNull來實現。如下所示:
3.Elvis 操作符
先看一段代碼:
val i: Int = if (b != null) b.length else -1 val i = b?.length ?: -1這兩行代碼表達的都是“如果b不等于null,i = b.length;如果b等于null,i = -1”。第一行代碼用的是if表達式,而第二行代碼使用了Elvis操作符,寫作?:。Elvis操作符表示如果?:左側表達式非空,就使用左側表達式,否則使用右側表達式。請注意,因為throw和return在Kotlin中都是表達式,所以它們也可以用在Elvis操作符右側。如下所示:
fun foo(node: Node): String? {val parent = node.getParent() ?: return nullval name = node.getName() ?: throw IllegalArgumentException("name expected")// …… }4. !! 操作符
!!操作符將任何值轉換為非空類型,若該值為空則拋出異常。如下所示:
var a = null a!! //運行代碼,拋出KotlinNullPointerException八、異常
Kotlin中所有異常類都是Throwable類的子類。每個異常都有消息、堆棧回溯信息和可選的原因。使用throw表達式可以拋出異常。舉個例子:
throw NullPointerException("NPE")使用try表達式可以捕獲異常。一個try表達式可以有多個catch代碼段;finally代碼段可以省略。舉個例子:
try {//捕獲異常 } catch (e: NullPointerException) {//異常處理 } catch (e: ClassNotFoundException) {//異常處理 } finally {//可選的finally代碼段 }因為Try是一個表達式,所以它可以有一個返回值。舉個例子:
val a: Int? = try {parseInt(input) } catch (e: NumberFormatException) {null }try表達式的返回值是 try塊中的最后一個表達式或者是catch塊中的最后一個表達式。finally塊中的內容不會影響表達式的結果。
九、類型別名
Kotlin提供類型別名來代替過長的類型名稱,這些類型別名不會引入新類型,且等效于相應的底層類型。可以通過使用關鍵字typealias修改類型別名,如下所示:
//使用關鍵字typealias修改類型別名Length //相當于 Length 就是一個 (String) -> Int 類型 typealias Length = (String) -> Int//調用 fun getLength(l: Length) = l("Czh") //編譯器把 Length 擴展為 (String) -> Int 類型 val l: Length = { it.length } println(getLength(l)) //輸出結果為:3使用類型別名能讓那些看起來很長的類型在使用起來變得簡潔,如下所示:
typealias MyType = (String, Int, Any, MutableList<String> ) -> Unit //當我們使用的時候 var myType:MyType //而不需要寫他原來的類型 //var myType:(String, Int, Any, MutableList<String> ) -> Unit總結
相對于Java來說,Kotlin有很多新的技術和語法糖,這也是為什么使用Kotlin來開發Android要優于Java。運用好這些新的東西,能大大加快開發速度。
原文鏈接:https://juejin.im/post/5a9cdc7df265da2393768e06
總結
以上是生活随笔為你收集整理的学习Kotlin(八)其他技术的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 学习Kotlin(七)反射和注解
- 下一篇: Kotlin的基本数值类型问题:是对象?