Kotlin快速入門(一)
最近了解一下Kotlin,畢竟Google力挺,網上對於Kotlin褒貶不一,有吹捧的,也有貶低的,在拿不定主意的時,不如自己動手試試,看看Kotlin會帶來哪些新的體驗,本文主要針對Java的開發者,著重總結兩者區別。
Kotlin的優勢
好好了解一下,不然被別人問為什麼使用Kotlin,會被問的啞口無言。
- 全面支援Lambda表示式
- 資料類(Data classes)
- 函式字面量和行內函數(Function literals & inline functions)
- 函式擴充套件(Extension function)
- 空安全(Null safety)
- 智慧轉換(Smart casts)
- 字串模版(String templates)
- 主建構函式(Primary constructors)
- 類委託(Class delegation)
- 型別判斷(Type inference)
- 單例(Singletons)
- 宣告點變數(Declaration-site variance)
- 區間表示式 (Range expressions)
Kotlin的基礎型別
1. 分隔符
Kotlin每行語句可以不以分號結束。
fun main(args: Array<String>) { println("Hello world") }
2. 註釋
多行註釋可以巢狀使用;單行註釋和文件註釋與Java一致。
fun main(args: Array<String>) { /*println("Hello world") /*println("first")*/ println("second")*/ println("third") }
簡書的Kotlin程式碼高亮有點問題,放到編譯器中的效果

註釋巢狀
3. 變數
- 宣告變數
var
和val
,val
是隻讀資料型別,只能賦值一次 - 型別推斷,可以不顯示宣告變數型別
fun main(args: Array<String>) { var i: Int // 顯示宣告變數i的型別,Java中的int a; var name: String = "Kotlin" // 顯示宣告變數並賦值,Java中的String name = "Kotlin"; var my_name = "JamFF" // 上面的簡寫,編譯器推斷Sting型別 name = "Java" // 重新賦值,Java中的name = "Java"; my_name = 18 // my_name是String型別,不能賦值為Int val a = false // 不可以重新賦值,Java中的final boolean b = false; val b: Int // 不同於Java,可以先宣告,不賦值 b = 1 // 只要在使用之前賦值即可,只能賦值一次 }
4. 整型
-
Byte
Java的byte
和Byte
,8bit。 -
Short
Java的short
和Short
,16bit。 -
Int
,Java的int
和Integer
,32bit。 -
Long
,Java的long
和Long
,64bit。
-
整型會預設型別推斷為
Int
,如果超過Int
取值範圍,會推斷為Long
fun main(args: Array<String>) { var a: Int = 18 a = 100_000_000_000 // 不能賦值,超過Int範圍 var b = 1// 整型預設推斷為Int,Byte,Short需要顯示宣告 var c = 100_000_000_000 // 如果超過Int範圍,會自動推斷型別為Long }
-
Kotlin是空安全的語言,所以上述四種資料型別均不能接受
null
值,如果要儲存null
值需要使用Byte?
、Short?
、Int?
、Long?
型別fun main(args: Array<String>) { val a: Int = null // 報錯 val b: Int? = null // 正確 }
-
Kotlin將不帶
?
型別對映為Java的基本型別,將帶?
型別對映為Java的引用型別。fun main(args: Array<String>) { val a: Int = 666 val b: Int = 666 // ===比較的是地址,==比較的是值 println(a === b) // true,基本型別比較 val c: Int? = 666 val d: Int? = 666 println(c === d) // false,引用型別比較 }
-
Kotlin支援二進位制、十進位制和十六進位制,不支援八進位制
fun main(args: Array<String>) { val a = 0b10101 val b = 0xab // a的值為21,b的值為171,a+b的值為192 println("a的值為$a,b的值為$b,a+b的值為${a + b}") }
5. 浮點型
與Java一致。
-
Float
:32bit,需要在尾部新增f
。 -
Double
:64bit,型別推斷的預設值。
fun main(args: Array<String>) { var a = 3.14 // 預設Double a = 2.0f // 報錯,不能賦值給Float var b = 1.5f // Float b = 3.14 // 報錯,不能賦值給Double }
6. 字元型
與Java不同,Kotlin的 Char
型變數不能當成整數值使用。
fun main(args: Array<String>) { var a: Char = 'a' // 字元a a = '\n' // 換行符 a = '\u8888' // 漢字'駕' a = 1 // 報錯 }
7. 數值型之間的型別轉換
-
Kotlin不支援取值範圍小的資料型別隱式轉換為取值範圍大的型別,需要顯式呼叫。
- toByte()
- toShort()
- toInt()
- toLong()
- toFloat()
- toDouble()
- toChar()
fun main(args: Array<String>) { var a: Byte = 1 var b: Short = 2 b = a // 報錯,Java允許 a = b.toByte() // 可以,注意溢位 b = a.toShort() // 可以 var c = 1.0f var d = 2.2 d = c // 報錯,Java允許 c = d.toFloat() // 可以,注意溢位 d = c.toDouble() // 可以 }
-
與Java一致,雖然Kotlin中缺乏隱式轉換,但在表示式中可以自動轉換。
fun main(args: Array<String>) { val a: Byte = 1 val b: Short = 2 val c = 1.0 val total = a + b // javaClass屬性來自Any型別,是Kotlin所有型別但根父類 println(total.javaClass) // int val total2 = a.toLong() + b.toByte() println(total2.javaClass) // long val total3 = a + b + c println(total3.javaClass) // double }
-
與Java一致,將浮點型強轉為整數型,小數部分會被截斷;將整數型強轉為浮點型沒有問題。
-
Kotlin中
Char
型雖然不能當成整數進行算術運算,但是可以Char
型值加、減一個整數值,也可以兩個Char
相減,但不能相加。fun main(args: Array<String>) { val a = 'a' val b = 'b' println(a + 2) // c println(b + 2) // d println(b - a) // 1 println(a + b) // 報錯 }
8. Boolean型別
與Java一致。
9. 空安全
-
非空型別和可空型別
fun main(args: Array<String>) { val str = "abc" val num: Int = str.toIntOrNull() // 報錯 val num2: Int? = str.toIntOrNull() // 通過 val num3 = str.toIntOrNull() // 通過,型別推斷為Int?型別 }
那麼Kotlin是如何保證空安全呢
fun main(args: Array<String>) { val a: String = "abc" val b: String? = "abc" println(a.length) // a非null,不可能出現空指標 println(b.length) // b可以為null,但是如果不判斷非空,編譯報錯 }
-
先判斷後使用
可空型別的變數不允許直接呼叫方法或屬性,必須判斷是否為null。
fun main(args: Array<String>) { val a: String? = "abc" // 可以使用安全呼叫和Elvis簡化 val len = if (a != null) a.length else -1 if (a != null && a.isNotEmpty()) { println(a.length) } else { println("空字串") } }
-
安全呼叫
- 可以使用
?.
進行安全呼叫,避免出現空指標。
fun main(args: Array<String>) { var a: String? = "abc" println(a?.length) // 列印4 a = null println(a?.length) // 列印null }
- 與Spring EL類似,Kotlin的安全呼叫也支援鏈式呼叫。
fun main(args: Array<String>) { // 安全的獲取user的dog的name,如果user或者user.dog為null,整個表示式返回null user?.dog?.name }
- 安全呼叫還可與let全域性函式結合使用。
fun main(args: Array<String>) { val arr: Array<String?> = arrayOf("abc", "JamFF", "Tom", null, "Tony") for (s in arr) { s?.let { println(it) } } }
- 可以使用
-
Elvis運算
?:
運算子就是Elvis,如果?:
左邊不為null
返回左邊表示式的值,否則返回?:
右邊表示式的值。fun main(args: Array<String>) { val a: String? = "abc" val len = if (a != null) a.length else -1 val len2 = a?.length ?: -1 // 上面語句的簡寫 if (a != null && a.isNotEmpty()) { println(a.length) } else { println("空字串") } }
Kotlin的
return
、throw
都屬於表示式,可以靈活使用。 -
強制呼叫
!!.
不管變數是否為null
強制呼叫,可以編譯通過,執行可能引發空指標,謹慎使用。fun main(args: Array<String>) { var a: String? = "abc" println(a!!.length) // 輸出3 a = null println(a!!.length) // 空指標 val arr: Array<String?> = arrayOf("abc", "JamFF", "Tom", null, "Tony") for (s in arr) { s!!.let { println(it) } // 空指標 } }
10. 字串
-
遍歷字串中每一個字元
fun main(args: Array<String>) { val str = "JamFF" for (c in str){ println(c) } }
-
字串分類
- 轉義字串,可以包含轉義字元,類似Java中的字串。
- 原始字串,可以包含換行符和任意文字,需要三個引號包裹。
fun main(args: Array<String>) { val str = "abc" // 轉移字串 // 原始字串 val txt = """ 但行好事, 勿問前程。 """ println(txt) // trimIndent去除字串前面的縮排 val txt2 = """ 但行好事, 勿問前程。 """.trimIndent() println(txt2) val txt3 = """ |但行好事, |勿問前程。 """ println(txt3) // trimMargin去除邊界符,Kotlin預設是"|", val txt4 = """ |但行好事, |勿問前程。 """.trimMargin() println(txt4) // 去除自定義邊界符 val txt5 = """ ^但行好事, ^勿問前程。 """.trimMargin("^") println(txt5) }
輸出結果
輸出結果
-
字串模版
Kotlin允許在字串(轉移字串、原始字串)中嵌入變數或表示式,只要放入
${}
中即可。fun main(args: Array<String>) { val a = 2018 var s = "今年是${a}年" println(s) s = "隨機數:${java.util.Random().nextInt(10)}" println(s) }
-
Kotlin字串的方法
11. 類型別名
- 類似於C語言的
typedef
的功能,Kotlin可以使用typealias
定義類型別名。fun main(args: Array<String>) { // Kotlin: Nested and local type aliases are not supported typealias StringSet = Set<String> // 報錯,別名不能寫在方法中 val set: StringSet var table: FileTable<String> } // 正確定義位置 typealias StringSet = Set<String> typealias FileTable<K> = MutableMap<K, MutableList<File>>
- 也可以給內部類定義別名。
class A { inner class Inner } class B { inner class Inner } typealias AInner = A.Inner typealias BInner = B.Inner fun main(args: Array<String>) { val a: AInner = A().Inner() val b = B().Inner() println(a.javaClass) // 輸出class A$Inner println(b.javaClass) // 輸出class B$Inner }
- Kotlin的
Lambda
表示式的型別直接就是函式型別,而Java的是函式是介面,因此Kotlin也允許為Lambda
表示式的型別指定別名。// 為(T) -> Boolean型別指定別名Predicate<T> typealias Predicate<T> = (T) -> Boolean fun main(args: Array<String>) { // 使用Predicate<String>定義變數,該變數的值是一個Lambda表示式 val p: Predicate<String> = { it.length > 4 } // 為filter()方法傳入p引數,只保留長度大於4的字串 println(arrayOf("Java", "PHP", "Python", "Go", "Kotlin").filter(p)) // 輸出[Python, Kotlin] }
重點
- 每行語句建議不以分號結束,換行即可。
- 可變變數
var
和不可變變數val
。 - 基本資料型別
- 空安全
- 字串和字串模版
- 類型別名