Kotlin 學(xué)習(xí)筆記(二)基本類型
這是一個(gè)Kotlin系列的教程,目的是為了使自己記憶和理解的更加深刻,將會(huì)添加對(duì)應(yīng)的Java代碼用于對(duì)比學(xué)習(xí)和更好的理解。
在Kotlin中,所有東西都是對(duì)象,在這個(gè)意義上講我們可以在任何變量上調(diào)用成員函數(shù)和屬性。一些類型可以有特殊的內(nèi)部表示--例如,數(shù)字、字符和布爾值可以在運(yùn)行時(shí)表示原生類型值,但是對(duì)于用戶來說,他們看起來就像是普通的類,在本節(jié)中,我們會(huì)描述Kotlin中使用的基本類型:數(shù)字、字符、布爾值、數(shù)組與字符串。
目錄
數(shù)字
Kotlin處理數(shù)字在某種程度上接近Java,但是并不完全相同。例如,對(duì)于數(shù)字沒有隱式擴(kuò)寬轉(zhuǎn)換(如Java中int可以隱式轉(zhuǎn)換為long),另外有些情況的字面值略有不同。
Kotlin提供了如下的內(nèi)置類型來表述數(shù)字(與Java很相似)
| Type | Bit width |
|---|---|
| Double | 64 |
| Float | 32 |
| Long | 64 |
| int | 32 |
| Short | 16 |
| Byte | 8 |
注意:在Kotlin中字符不是數(shù)字
字面常量
數(shù)值常量字面值有一下幾種:
- 十進(jìn)制:
123-
Long類型用大寫L標(biāo)記:
123L
-
Long類型用大寫L標(biāo)記:
- 十六進(jìn)制:
0x0F - 二進(jìn)制:
0b0001011
注意:不支持八進(jìn)制
Kotlin同樣支持浮點(diǎn)數(shù)的常規(guī)表示方法:
- 默認(rèn) Double:
123.5、123.5e10 - Float 用
f或者F標(biāo)記:123.5f
數(shù)字字面值中的下劃線(自 1.1 起)
你可以使用下劃線使數(shù)字常量更易讀:
val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010
表示方式
在 Java 平臺(tái)數(shù)字是物理存儲(chǔ)為 JVM 的原生類型,除非我們需要一個(gè)可空的引用(如 Int?)或泛型。 后者情況下會(huì)把數(shù)字裝箱。
注意數(shù)字裝箱不必保留同一性:
val a: Int = 10000
print(a === a) // 輸出“true”
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA === anotherBoxedA) // !?。≥敵觥癴alse”?。?!
另一方面,它保留了相等性:
val a: Int = 10000
print(a == a) // 輸出“true”
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA == anotherBoxedA) // 輸出“true”
顯式轉(zhuǎn)換
由于不同的表示方式,較小類型并不是較大類型的子類型。 如果它們是的話,就會(huì)出現(xiàn)下述問題:
// 假想的代碼,實(shí)際上并不能編譯:
val a: Int? = 1 // 一個(gè)裝箱的 Int (java.lang.Integer)
val b: Long? = a // 隱式轉(zhuǎn)換產(chǎn)生一個(gè)裝箱的 Long (java.lang.Long)
print(b == a) // 驚!這將輸出“false”鑒于 Long 的 equals() 會(huì)檢測另一個(gè)是否也為 Long
所以相等性會(huì)在所有地方悄無聲息地失去,更別說同一性了。
因此較小的類型不能隱式轉(zhuǎn)換為較大的類型。 這意味著在不進(jìn)行顯式轉(zhuǎn)換的情況下我們不能把 Byte 型值賦給一個(gè) Int 變量。
val b: Byte = 1 // OK, 字面值是靜態(tài)檢測的
val i: Int = b // 錯(cuò)誤
我們可以顯式轉(zhuǎn)換來拓寬數(shù)字
val i: Int = b.toInt() // OK: 顯式拓寬
每個(gè)數(shù)字類型支持如下的轉(zhuǎn)換:
- toByte(): Byte
- toShort(): Short
- toInt(): Int
- toLong(): Long
- toFloat(): Float
- toDouble(): Double
- toChar(): Char
缺乏隱式類型轉(zhuǎn)換并不顯著,因?yàn)轭愋蜁?huì)從上下文推斷出來,而算術(shù)運(yùn)算會(huì)有重載做適當(dāng)轉(zhuǎn)換,例如:
val l = 1L + 3 // Long + Int => Long
運(yùn)算
Kotlin支持?jǐn)?shù)字運(yùn)算的標(biāo)準(zhǔn)集,運(yùn)算被定義為相應(yīng)的類成員(但編譯器會(huì)將函數(shù)調(diào)用優(yōu)化為相應(yīng)的指令)。 參見運(yùn)算符重載。
對(duì)于位運(yùn)算,沒有特殊字符來表示,而只可用中綴方式調(diào)用命名函數(shù),例如:
val x = (1 shl 2) and 0x000FF000
這是完整的位運(yùn)算列表(只用于 Int 和 Long):
- shl(bits) – 有符號(hào)左移 (Java 的 <<)
- shr(bits) – 有符號(hào)右移 (Java 的 >>)
- ushr(bits) – 無符號(hào)右移 (Java 的 >>>)
- and(bits) – 位與
- or(bits) – 位或
- xor(bits) – 位異或
- inv() – 位非
浮點(diǎn)數(shù)比較
本節(jié)討論的浮點(diǎn)數(shù)操作如下:
相等性檢測:a == b 與a != b
比較操作符:a < b、 a > b、 a <= b、 a >= b
區(qū)間實(shí)例以及區(qū)間檢測:
a..b、 x in a..b、 x !in a..b
當(dāng)其中的操作數(shù) a 與b都是靜態(tài)已知的 Float 或 Double 或者它們對(duì)應(yīng)的可空類型(聲明為該類型,或者推斷為該類型,或者智能類型轉(zhuǎn)換的結(jié)果是該類型),兩數(shù)字所形成的操作或者區(qū)間遵循 IEEE 754浮點(diǎn)運(yùn)算標(biāo)準(zhǔn)。
然而,為了支持泛型場景并提供全序支持,當(dāng)這些操作符并非靜態(tài)類型為浮點(diǎn)數(shù)(例如是 Any、 Comparable<……>、 類型參數(shù))時(shí),這些操作使用為 Float 與 Double 實(shí)現(xiàn)的不符合標(biāo)準(zhǔn)的 equals 與 compareTo,這會(huì)出現(xiàn):
認(rèn)為 NaN 與其自身相等
認(rèn)為 NaN 比包括正無窮大(POSITIVE_INFINITY)在內(nèi)的任何其他元素都大
認(rèn)為 -0.0小于 0.0
字符
字符用 Char 類型表示。它們不能直接當(dāng)作數(shù)字
fun check(c: Char) {
if (c == 1) { // 錯(cuò)誤:類型不兼容
// ……
}
}
字符字面值用單引號(hào)括起來: '1'。 特殊字符可以用反斜杠轉(zhuǎn)義。 支持這幾個(gè)轉(zhuǎn)義序列:\t、 \b、\n、\r、'、"、\和$。 編碼其他字符要用 Unicode 轉(zhuǎn)義序列語法:'\uFF00'。
我們可以顯式把字符轉(zhuǎn)換為 Int 數(shù)字:
fun decimalDigitValue(c: Char): Int {
if (c !in '0'..'9')
throw IllegalArgumentException("Out of range")
return c.toInt() - '0'.toInt() // 顯式轉(zhuǎn)換為數(shù)字
}
當(dāng)需要可空引用時(shí),像數(shù)字、字符會(huì)被裝箱。裝箱操作不會(huì)保留同一性。
布爾
布爾用 Boolean 類型表示,它有兩個(gè)值:true 和 false。
若需要可空引用布爾會(huì)被裝箱。
內(nèi)置的布爾運(yùn)算有:
- || – 短路邏輯或
- && – 短路邏輯與
- ! - 邏輯非
數(shù)組
數(shù)組在 Kotlin 中使用 Array 類來表示,它定義了 get 和 set 函數(shù)(按照運(yùn)算符重載約定這會(huì)轉(zhuǎn)變?yōu)?[])和 size 屬性,以及一些其他有用的成員函數(shù):
class Array<T> private constructor() {
val size: Int
operator fun get(index: Int): T
operator fun set(index: Int, value: T): Unit
operator fun iterator(): Iterator<T>
// ……
}
我們可以使用庫函數(shù) arrayOf() 來創(chuàng)建一個(gè)數(shù)組并傳遞元素值給它,這樣 arrayOf(1, 2, 3) 創(chuàng)建了 array [1, 2, 3]。 或者,庫函數(shù) arrayOfNulls() 可以用于創(chuàng)建一個(gè)指定大小的、所有元素都為空的數(shù)組。
另一個(gè)選項(xiàng)是用接受數(shù)組大小和一個(gè)函數(shù)參數(shù)的 Array 構(gòu)造函數(shù),用作參數(shù)的函數(shù)能夠返回給定索引的每個(gè)元素初始值:
// 創(chuàng)建一個(gè) Array<String> 初始化為 ["0", "1", "4", "9", "16"]
val asc = Array(5, { i -> (i * i).toString() })
如上所述,[] 運(yùn)算符代表調(diào)用成員函數(shù) get() 和 set()。
注意: 與 Java 不同的是,Kotlin 中數(shù)組是不型變的(invariant)。這意味著 Kotlin 不讓我們把 Array<String>賦值給 Array<Any>,以防止可能的運(yùn)行時(shí)失?。ǖ悄憧梢允褂?Array<out Any>)。
Kotlin 也有無裝箱開銷的專門的類來表示原生類型數(shù)組: ByteArray、 ShortArray、IntArray 等等。這些類和 Array 并沒有繼承關(guān)系,但是它們有同樣的方法屬性集。它們也都有相應(yīng)的工廠方法:
val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]
字符串
字符串用 String 類型表示。字符串是不可變的。 字符串的元素——字符可以使用索引運(yùn)算符訪問: s[i]。 可以用 for 循環(huán)迭代字符串:
for (c in str) {
println(c)
}
可以用 +操作符連接字符串。這也適用于連接字符串與其他類型的值, 只要表達(dá)式中的第一個(gè)元素是字符串:
val s = "abc" + 1
println(s + "def")
請(qǐng)注意,在大多數(shù)情況下,優(yōu)先使用字符串模板或原始字符串而不是字符串連接。
字符串字面值
Kotlin 有兩種類型的字符串字面值: 轉(zhuǎn)義字符串可以有轉(zhuǎn)義字符,以及原始字符串可以包含換行和任意文本。轉(zhuǎn)義字符串很像 Java 字符串:
val s = "Hello, world!\n"
轉(zhuǎn)義采用傳統(tǒng)的反斜杠方式。參見上面的 字符 查看支持的轉(zhuǎn)義序列。
原始字符串 使用三個(gè)引號(hào)(""")分界符括起來,內(nèi)部沒有轉(zhuǎn)義并且可以包含換行和任何其他字符:
val text = """
for (c in "foo")
print(c)
"""
你可以通過 trimMargin() 函數(shù)去除前導(dǎo)空格:
val text = """
|Tell me and I forget.
|Teach me and I remember.
|Involve me and I learn.
|(Benjamin Franklin)
""".trimMargin()
默認(rèn) | 用作邊界前綴,但你可以選擇其他字符并作為參數(shù)傳入,比如 trimMargin(">")。
字符串模板
字符串可以包含模板表達(dá)式 ,即一些小段代碼,會(huì)求值并把結(jié)果合并到字符串中。 模板表達(dá)式以美元符($)開頭,由一個(gè)簡單的名字構(gòu)成:
val i = 10
println("i = $i") // 輸出“i = 10”
或者用花括號(hào)括起來的任意表達(dá)式:
val s = "abc"
println("$s.length is ${s.length}") // 輸出“abc.length is 3”
原始字符串和轉(zhuǎn)義字符串內(nèi)部都支持模板。 如果你需要在原始字符串中表示字面值 $ 字符(它不支持反斜杠轉(zhuǎn)義),你可以用下列語法:
val price = """
${'$'}9.99
"""