目錄
一 概述
Android 1.5系統中Google 引入了NDK功能,支持使用C和C++語言來進行一些本地化開發。
17年官宣kotlin爲安卓一級開發語言;所以對於安卓開發者,學習kotlin是必須的,這是java最好的第三方庫(富語法糖java)。
kotlin將代碼編譯成同樣規格的class文件讓Java虛擬機識別,它繼承了Java的全部財富,和Java100%兼容的,可以直接調用使用Java編寫的代碼,也可以無縫使用Java第三方的開源庫;Kotlin的類型推斷也在Java 8 中被推出,Lambda作爲函數式編程的基礎也在Java 8版本中加入,兩者會越來越像。
Kotlin是函數式編程語言(注1:),Kotlin的語法非常像Scala,python,借鑑了很多語言,學了很多語言發現他們越來越像,就像C#之父Anders Hejlsberg說的:未來的編程語言將逐漸融合各自的特性,而不會只存在單純的聲明式語言或者函數編程語言。
對比Java的一些優勢:
與作爲命令式語言時代的產物:Java相比,Kotlin對內聯函數的支持,使它運行Lambda表達式更快;
提前到編譯期的空指針安全檢測;
Kotlin有很多現代靜態編程語言的特點:類型推斷、多範式支持、可空性表達、擴展函數、DSL支持等。這些功能Java也陸續在加入。
二 基本類型
Kotlin完全拋棄了Java中的基本數據類型,全部使用了對象數據類型,在 Kotlin 中,所有東西都是對象;
2.1 數字
基本Kotlin數值類型包括:Byte、Short、Int、Long、Float、Double等。與Java不同的是,Kotlin中的Charactor不屬於數值類型。
Kotlin 提供了一組表示數字的內置類型。 對於整數,有四種不同大小的類型,因此值的範圍也不同。
類型 | 大小(比特數) | 最小值 | 最大值 |
---|---|---|---|
Byte | 8 | -128 | 127 |
Short | 16 | -32768 | 32767 |
Int | 32 | -2,147,483,648 (-231) | 2,147,483,647 (231 - 1) |
Long | 64 | -9,223,372,036,854,775,808 (-263) | 9,223,372,036,854,775,807 (263 - 1) |
所有以未超出 Int
最大值的整型值初始化的變量都會推斷爲 Int
類型。如果初始值超過了其最大值,那麼推斷爲 Long
類型。 如需顯式指定 Long
型值,請在該值後追加 L
後綴。
val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1
對於浮點數,Kotlin 提供了 Float
與 Double
類型。
類型 | 大小(比特數) | 有效數字比特數 | 指數比特數 | 十進制位數 |
---|---|---|---|---|
Float | 32 | 24 | 8 | 6-7 |
Double | 64 | 53 | 11 | 15-16 |
對於以小數初始化的變量,編譯器會推斷爲 Double
類型。 如需將一個值顯式指定爲 Float
類型,請添加 f
或 F
後綴。 如果這樣的值包含多於 6~7 位十進制數,那麼會將其舍入。
val pi = 3.14 // Double
val e = 2.7182818284 // Double
val eFloat = 2.7182818284f // Float,實際值爲 2.7182817
請注意,與一些其他語言不同,Kotlin 中的數字沒有隱式拓寬轉換。 例如,具有 Double
參數的函數只能對 Double
值調用,而不能對 Float
、 Int
或者其他數字值調用。
注:=== 表示比較對象地址,== 表示比較兩個值大小。
2.2 字符類型
Kotlin中字符類型用Charactor表示,與Java不同的是,它們不能直接當作數字,它無法直接使用Char類型的ASCII進行算數運算。如果需要用,得用toInt函數轉爲相應ASCII。
2.3 布爾型
一些語言比如OC或python中,false、true可以用0、1代替,在Kotlin/Java中不允許。
2.4 數組類型
Kotlin中數組用Array類表示,可以使用庫函數 arrayOf()
來創建一個數組並傳遞元素值給它。Kotlin 也有無裝箱開銷的專門的類來表示原生類型數組: ByteArray
、 ShortArray
、IntArray
等等。這些類與 Array
並沒有繼承關係,但是它們有同樣的方法屬性集。
// 大小爲 5、值爲 [0, 0, 0, 0, 0] 的整型數組
val arr = IntArray(5)
// 例如:用常量初始化數組中的值
// 大小爲 5、值爲 [42, 42, 42, 42, 42] 的整型數組
val arr = IntArray(5) { 42 }
// 例如:使用 lambda 表達式初始化數組中的值
// 大小爲 5、值爲 [0, 1, 2, 3, 4] 的整型數組(值初始化爲其索引值)
var arr = IntArray(5) { it * 1 }
2.5 字符串
Kotlin中字符串同Java,用String表示。
字符串用 String
類型表示。字符串是不可變的。 字符串的元素——字符可以使用索引運算符訪問: s[i]
。 可以用 for 循環迭代字符串。
Kotlin提供了字符串的內嵌表達式,也被稱爲字符串模板。語法規則:
"hello, ${obj.name}. nice to meet you!"
字符串裏嵌入${}這種語法結構的表達式,並在運行時使用表達式執行的結果替代這一部分內容,當表達式中僅有一個變量的時候,還可以將兩邊的大括號省略:
"hello, $name. nice to meet you!"
三 類型轉換和變量定義
3.1 類型轉換
精度缺失的問題就不多說了,一般兩個不同類型數字在邏輯運算時並不需要特別注意,算術運算符會重載適應不同類型:
val a = 1L + 3 // Long + Int => Long
顯示轉換:
類型 | 顯示轉換 |
Byte | toByte() |
Short | toShort() |
Int | toInt() |
Long | toLong() |
Float | toFloat() |
Double | toDouble() |
Char | toChar() |
String | toString() |
3.2 變量
Kotlin中定義一個變量,只允許在變量前聲明兩種關鍵字:val和var。
- val(value的簡寫)用來聲明一個不可變的變量,這種變量在初始賦值之後就再也不能重新賦值,對應Java中的final變量。
- var(variable的簡寫)用來聲明一個可變的變量,這種變量在初始賦值之後仍然可以再被重新賦值,對應Java中的非final變量。
這樣設計,是爲了解決Java中final關鍵字沒有被合理使用的問題。
使用指導:永遠優先使用val來聲明一個變量,而當val沒有辦法滿足你的需求時再使用var。
Kotlin的類型推導機制:如下,val關鍵字定義了一個變量a賦值爲10,這裏a就會被自動推導成整型變量。
fun main() {
val a = 10
println("a = " + a)
}
但對一個變量延遲賦值的話,Kotlin就無法自動推導它的類型了。這時候就需要顯式地聲明變量類型才行:
val a: Int = 10
四 函數
Kotlin函數語法規則:
fun methodName(param1: Int, param2: Int): Int {
return 0
}
參數括號後面的部分用於聲明該函數會返回什麼類型的數據,上例表示該函數會返回一個Int類型的數據。
如果一個函數不返回任何有用的值,它的返回類型是 Unit
。如果函數不需要返回任何數據,這部分可以不寫。
當一個函數中只有一行代碼時,Kotlin允許我們不必編寫函數體,可以直接將唯一的一行代碼寫在函數定義的尾部,中間用等號連接即可:
fun largerNumber(num1: Int, num2: Int): Int = max(num1, num2)
再結合Kotlin出色的類型推導機制,由於max()函數返回的是一個Int值,因此Kotlin可以推導出largerNumber()函數返回的必然也是一個Int值,代碼可以進一步簡化成如下形式:
fun largerNumber(num1: Int, num2: Int) = max(num1, num2)
默認參數:
函數參數可以有默認值,當省略相應的參數時使用默認值。與其他語言相比,這可以減少重載數量:
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) { /*……*/ }
如果在默認參數之後的最後一個參數是 lambda表達式,那麼它既可以作爲具名參數在括號內傳入,也可以在括號外傳入:
fun foo(bar: Int = 0, baz: Int = 1, qux: () -> Unit) { /*……*/ }
foo(1) { println("hello") } // 使用默認值 baz = 1
foo(qux = { println("hello") }) // 使用兩個默認值 bar = 0 與 baz = 1
foo { println("hello") } // 使用兩個默認值 bar = 0 與 baz = 1
Kotlin函數的可見性修飾符和Java相比變化較大。
Java和Kotlin中函數可見性修飾符之間的區別:
修飾符 | Java | Kotlin |
public | 所有類可見 | (默認類型)所有類可見 |
private | 當前類可見 | 當前類可見 |
protected | 當前類、子類、同一包路徑下的類可見 | 當前類、子類可見 |
default | (默認類型)同一包路徑下的類可見 | 無 |
internal | 無 | 同一模塊中的類可見 |
五 控制流:if、when、for、while
5.1 if條件語句
fun largerNumber(num1: Int, num2: Int): Int {
var value = 0
if (num1 > num2) {
value = num1
} else {
value = num2
}
return value
}
Kotlin中的if語句相比於Java有一個額外的功能,它是可以有返回值的,返回值就是if語句每一個條件中最後一行代碼的返回值。因此,上述代碼就可以簡化成如下形式:
fun largerNumber(num1: Int, num2: Int): Int {
return if (num1 > num2) {
num1
} else {
num2
}
}
kotlin的語法糖裏,當一個函數只有一行代碼時,可以省略函數體部分,直接將這一行代碼使用等號串連在函數定義的尾部。雖然上述代碼中的largerNumber()函數不止只有一行代碼,但是它和只有一行代碼的作用是相同的,只是返回了一下if語句的返回值而已,符合該語法糖的使用條件。那麼我們就可以將代碼進一步精簡:
fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) {
num1
} else {
num2
}
再精練:
fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) num1 else num2
5.2 when條件語句
Kotlin中的when語句類似於Java中的switch語句,但比Java中的switch語句要靈活很多。Java中的switch只能傳入整型或短於整型的變量作爲條件,JDK 1.7之後增加了對字符串變量的支持。
編寫一個查詢考試成績的功能,輸入一個學生的姓名,返回該學生考試的分數,if寫法:
fun getScore(name: String) = if (name == "Tom") {
86
} else if (name == "Jim") {
77
} else if (name == "Jack") {
95
} else if (name == "Lily") {
100
} else {
0
}
when語句允許傳入一個任意類型的參數,然後可以在when的結構體中定義一系列的條件,格式是:
匹配值 -> { 執行邏輯 }
當你的執行邏輯只有一行代碼時,{ }可以省略。
當使用when語句的時候,現在我們將代碼改成如下寫法:
fun getScore(name: String) = when (name) {
"Tom" -> 86
"Jim" -> 77
"Jack" -> 95
"Lily" -> 100
else -> 0
}
除了精確匹配之外,when語句還允許進行類型匹配。舉個例子。定義一個checkNumber()函數,如下所示:
fun checkNumber(num: Number) {
when (num) {
is Int -> println("number is Int")
is Double -> println("number is Double")
else -> println("number not support")
}
}
上述代碼中,is關鍵字就是類型匹配的核心,它相當於Java中的instanceof關鍵字。
由於checkNumber()函數接收一個Number類型的參數,這是Kotlin內置的一個抽象類,像Int、Long、Float、Double等與數字相關的類都是它的子類,所以就裏就可以使用類型匹配來判斷傳入的參數到底屬於什麼類型,如果是Int型或Double型,就將該類型打印出來,否則就打印不支持該參數的類型。
when語句還有一種不帶參數的用法,雖然這種用法可能不太常用,但有的時候卻能發揮很強的擴展性。
拿剛纔的getScore()函數舉例,如果我們不在when語句中傳入參數的話,還可以這麼寫:
fun getScore(name: String) = when {
name == "Tom" -> 86
name == "Jim" -> 77
name == "Jack" -> 95
name == "Lily" -> 100
else -> 0
}
可以看到,這種用法是將判斷的表達式完整地寫在when的結構體當中。注意,Kotlin中判斷字符串或對象是否相等可以直接使用==關鍵字,而不用像Java那樣調用equals()方法。可能你會覺得這種無參數的when語句寫起來比較冗餘,但有些場景必須使用這種寫法才能實現。舉個例子,假設所有名字以Tom開頭的人,他的分數都是86分,這種場景如果用帶參數的when語句來寫就無法實現,而使用不帶參數的when語句就可以這樣寫:
fun getScore(name: String) = when {
name.startsWith("Tom") -> 86
name == "Jim" -> 77
name == "Jack" -> 95
name == "Lily" -> 100
else -> 0
}
現在不管你傳入的名字是Tom還是Tommy,只要是以Tom開頭的名字,他的分數就是86分。
5.3 For循環
Java中主要有兩種循環語句:while循環和for循環。Kotlin的while循環和Java中的while循環沒有任何區別。
Kotlin在for循環方面做了很大幅度的修改,Java中最常用的for-i循環在Kotlin中直接被捨棄了,而Java中另一種for-each循環則被Kotlin進行了大幅度的加強,變成了for-in循環,所以我們只需要學習for-in循環的用法就可以了。
區間的概念
閉區間:[0, 10]
val range = 0..10
左閉右開區間:[0,10):
val range = 0 until 10
有了區間之後,我們就可以通過for-in循環來遍歷這個區間,比如在main()函數中編寫如下代碼:
fun main() {
for (i in 0..10) {
println(i)
}
}
默認情況下,for-in循環每次執行循環時會在區間範圍內遞增1,相當於Java for-i循環中i++的效果,而如果你想跳過其中的一些元素,可以使用step關鍵字:
fun main() {
for (i in 0 until 10 step 2) {
println(i)
}
}
上述代碼表示在遍歷[0, 10)這個區間的時候,每次執行循環都會在區間範圍內遞增2,相當於for-i循環中i = i + 2的效果。
.. 和until關鍵字都要求區間的左端必須小於等於區間的右端,也就是這兩種關鍵字創建的都是一個升序的區間。如果你想創建一個降序的區間,可以使用downTo關鍵字,用法如下:
fun main() {
for (i in 10 downTo 1) {
println(i)
}
}
這裏我們創建了一個[10, 1]的降序區間。
標註1:
函數式編程是一種編程範式,我們常見的編程範式有命令式編程(Imperative programming),函數式編程,邏輯式編程,常見的面向對象編程是也是一種命令式編程。更多瞭解推薦參考:https://www.zhihu.com/question/28292740
學習參考
1 官網文檔 https://www.kotlincn.net/docs/reference/basic-syntax.html
2 郭霖:《第一行代碼》