1. 程式人生 > >學習Kotlin(八)其他技術

學習Kotlin(八)其他技術

目錄

一、解構宣告
二、區間
三、型別檢查與轉換
四、this表示式
五、相等性
六、操作符過載
七、空安全
八、異常
九、類型別名

一、解構宣告

解構宣告能同時建立多個變數,將物件中的資料解析成相對的變數。舉個例子:

//建立一個數據類User
data class User(var name: String, var age: Int)

//獲得User的例項
var user = User("Czh", 22)
//宣告變數 name 和 age
var (name, age) = user

println("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鍵值對,通過解構宣告可以把這些值解構出來。如下所示:
var map = mutableMapOf<String, Any>()
map.put("name", "Czh")
map.put("age", 22)
for ((key, value) in map) {
    println("$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) 
//輸出531

4.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.length
    is 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
//丟擲ClassCastException

4.可空轉換操作符

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(一個類或者擴充套件函式, 或者帶標籤的帶接收者的函式字面值)我們使用[email protected],其中 @label 是一個代指this來源的標籤。舉個例子:
class A { // 隱式標籤 @A
    inner class B { // 隱式標籤 @B
        fun Int.foo() { // 隱式標籤 @foo
            val a = [email protected] // A 的 this
            val b = [email protected] // B 的 this

            val c = this // foo() 的接收者,一個 Int
            val c1 = [email protected] // foo() 的接收者,一個 Int

            val funLit = [email protected] fun String.() {
                val d = this // funLit 的接收者
            }


            val funLit2 = { s: String ->
                // foo() 的接收者,因為它包含的 lambda 表示式
                // 沒有任何接收者
                val d1 = 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 = null

2.安全呼叫操作符

接著上面的程式碼,如果你呼叫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,如下所示:
val i: Int? = i as? Int
  • 可空型別的集合
    如果你有一個可空型別元素的集合,並且想要過濾非空元素,你可以使用filterNotNull來實現。如下所示:
val nullableList: List<Int?> = listOf(1, 2, null, 4)
val intList: List<Int> = nullableList.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操作符表示如果?:左側表示式非空,就使用左側表示式,否則使用右側表示式。請注意,因為throwreturn在Kotlin中都是表示式,所以它們也可以用在Elvis操作符右側。如下所示:

fun foo(node: Node): String? {
    val parent = node.getParent() ?: return null
    val 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。運用好這些新的東西,能大大加快開發速度。

相關推薦

學習Kotlin()其他技術

目錄 一、解構宣告 二、區間 三、型別檢查與轉換 四、this表示式 五、相等性 六、操作符過載 七、空安全 八、異常 九、類型別名 一、解構宣告 解構宣告能同時建立多個變數,將物件中的資料解析成相對的變數。舉個例子: //建立一個數據類User da

SQL註入學習總結():其他SQL註入的異或註入

href http 發現 .sql 異或 ascii 3.2 運算 證明 其他類型註入的詳解(5) 5.sql異或註入 背景當我們在嘗試SQL註入時,發現union,and被完全過濾掉了,就可以考慮使用異或註入 知識點 異或運算規則: 1^1=0 0^0=0 0^1=1 1

linux系統學習天-<<工程師技術>>

linux工程師技術 linxu管理員技術 linux雲計算 深圳雲計算王森 雲計算運維工程師 兩臺虛擬機,均修改防火器與主機名虛擬機server0: # firewall-cmd --set-default-zone=trusted # echo server0.example.com

從零開始學習音視訊程式設計技術)FFMPEG Qt視訊播放器之音視訊同步

前面分別講解了: 現在我們就將視訊和音訊合併,並讓聲音和畫面同步。 加入音訊的部分就不做講解了,這裡主要講下聲音和視訊同步的步驟。 首先剛開始播放的時候通過av_gettime()獲取系統主時鐘,記錄下來。 以後便不斷呼叫av_gettime()獲取系統時鐘

Java基礎知識二次學習--第章 流

cti 註意 spa 基礎 2個 cnblogs images 方向 視頻 第八章 流 時間:2017年4月28日11:03:07~2017年4月28日11:41:54 章節:08章_01節 視頻長度:21:15 內容:IO初步 心得: 所有的流在java.io包裏面

初學者如何選擇學習哪種測試技術

是什麽 碎片 場景 數據 品牌 聲明 答案 基本 性能問題 百度搜索:小強測試品牌 挨踢脫口秀,將技術娛樂化,碎片系統化,盡在荔枝FM 本文節選自《小強軟件測試瘋狂講義》一書 如果對你有一丟丟的幫助,歡迎轉發本文 這個話題有點沈重,因為一旦表述不好肯定會被一些無良的人罵

JAVA學習

while ring 繼續 break 默認值 tin 都是 遍歷 次數 二重循環 一、回顧3種循環結構 1、while 語法 條件表達式的初始值; while(條件表達式){ 循環操作; 更改條件表達式的語句; } 特點:先判斷,再執行,有可能一次循環都沒有

HBase概念學習)開發一個類twitter系統之表設計

至少 創建用戶 列表 ase wke long 少包 mali 。。 這邊文章先將可能的需求分析一下,設計出HBase表,下一步再開始編寫client代碼。 TwiBase系統 1、背景 為了加深HBase基本概念的學習,參考HBase實戰這本書實際動手做了這個樣

線程學習--()queue

高性能 數組 pre clas 並行 lin 性能 sync 緩沖 http://www.cnblogs.com/sigm/p/6186401.html 一、ConcurrentLinkedQueue 是一個適用於高並發場景下的隊列,通過無鎖的方式,實現了高並發狀態下的高性

Shell 腳本學習筆記:流程控制

等於 break 語句 until循環 ase 所有 span 數字 if 語句 一、 if else /// 如果else分支沒有語句執行,就不要寫這個else 1、if 語句 if condition then

Halcon學習)文本操作

文件的 dmi filename 標簽 add .com all sea min 標簽: 學習 雜談 分類: halcon學習 1.open_file( : : FileName, FileType : FileHand

如何快速學習一門新技術(轉載)

修改 操作 如何快速 mongodb rom try 深入 tro 分享 前幾天fork了Ruby China的源碼,面對陌生的Ruby技術棧,一頭霧水。 我fork它並不單為了學習,而是要在最短的時間搭建起我腦海中的社區網站。所以我不可能針對每一門新技術都去買一本

Java基礎學習筆記 Java基礎語法之接口和多態

java cas 發現 過程 類類型 結果 覆寫 實例 new 接口 接口概念 接口是功能的集合,同樣可看做是一種數據類型,是比抽象類更為抽象的”類”。接口只描述所應該具備的方法,並沒有具體實現,具體的實現由接口的實現類(相當於接口的子類)來完成

JavaWeb學習筆記五 會話技術Cookie&Session

function for type getwriter 信息 web資源 案例 utf-8 template 什麽是會話技術? 例如網站的購物系統,用戶將購買的商品信息存儲到哪裏?因為Http協議是無狀態的,也就是說每個客戶訪問服務器端資源時,服務器並不知道該客戶端是誰,所

算法學習

讓我 卡片 ans return ack 好的 log 編程 其中 1.Combinations Counting(組合計數) 說明:我們有一個組合的例子——從給定的集合中選擇幾個元素的不同方法(不考慮順序)。例如,如果這個男孩有4個糖果(不同種類的糖果),並且只取其中的2

學習編程,技術那麽多,如何選擇呢?

fhq tps ict 不同的 img use tex com mx2 2017-08-31 編程譯站 所謂“知也無涯,生也有涯”,我們精力有限,在學習技術的事情上,建議大家選擇一個技術領域專攻下去,其他知識也要做相應了解。

java核心學習(十) javaNIO框架---“塊”模型的IO

rac tag pmod 輸出流 decode 非阻塞 實際應用 byte channel 一、java新IO概述   javaIO中的輸入流和輸出流都是通過字節的移動來處理的,面向流的輸入輸出系統一次只能處理一個字節,因此效率不高,而且傳統的輸入輸出流是阻塞試的,也就是說

Maven學習(十)-----Maven依賴管理

oca 子項目 sta stage clu padding 類型 type 核心 其中一個Maven的核心特征是依賴管理。管理依賴關系變得困難的任務一旦我們處理多模塊項目(包含數百個模塊/子項目)。 Maven提供了一個高程度的控制來管理這樣的場景。 傳遞依賴發現 這是很通

Java學習筆記---類的靜態變量與靜態方法的訪問與調用方式

solved 對象 getname cannot hang variable bsp protected some 靜態變量又稱類變量,靜態方法又稱類方法,它們統稱為靜態成員或類成員。靜態成員由static修飾,是屬於整個類的,所有的對象共享這些靜態成員。不需要創建任何對象

基於深度學習的病毒檢測技術無需沙箱環境,直接將樣本文件轉換為二維圖片,進而應用改造後的卷積神經網絡 Inception V4 進行訓練和檢測

進制 思科 開發 主題 需求 做的 病毒 無法 大於 話題 3: 基於深度學習的二進制惡意樣本檢測 分享主題:全球正在經歷一場由科技驅動的數字化轉型,傳統技術已經不能適應病毒數量飛速增長的發展態勢。而基於沙箱的檢測方案無法滿足 APT 攻擊的檢測需求,也受到多種反沙箱技術的