Kotlin 中择诈,我們可以調(diào)用任何變量的成員函數(shù)和屬性凡蚜,從這個(gè)角度來(lái)說(shuō),一切皆對(duì)象吭从。某些類(lèi)型可以有特殊的內(nèi)部表現(xiàn) - 例如,數(shù)字恶迈、字符和布爾型在運(yùn)行時(shí)可以表現(xiàn)為基礎(chǔ)類(lèi)型(primitive types)涩金,但是對(duì)用戶(hù)來(lái)說(shuō)谱醇,他們看上去就是是普通的類(lèi)。這一章節(jié)主要描述 Kotlin 的基本類(lèi)型:數(shù)字步做、字符副渴、布爾、數(shù)組和字符串全度。
數(shù)值
Kotlin 處理數(shù)字的方式與 Java 類(lèi)似煮剧,但不是完全一致。例如将鸵,數(shù)值沒(méi)有隱式的拓寬轉(zhuǎn)換(implicit widening conversions)勉盅,某些情況下,字面意思也會(huì)稍有不同顶掉。
Kotlin 提供了如下內(nèi)置類(lèi)型來(lái)表示數(shù)值(接近 Java):
類(lèi)型 | 位寬 |
---|---|
Double | 64 |
Float | 32 |
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
注意:字符不是一種數(shù)值草娜。
字面常量
整形值的字面常量有如下形式:
- 十進(jìn)制:
123
- 長(zhǎng)整型用
L
做標(biāo)記:123L
- 長(zhǎng)整型用
- 十六進(jìn)制:
0x0F
- 二進(jìn)制:
0b00001011
注意:不支持八進(jìn)制。
浮點(diǎn)數(shù)也支持約定的標(biāo)記:
- double 類(lèi)型:
123.5
痒筒,123.5e10
- float 用
f
或者F
標(biāo)記:123.5f
數(shù)值字面值中的下劃線(xiàn)(1.1開(kāi)始)
下劃線(xiàn)可以使數(shù)值常量更具可讀性:
val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_D5_5E
val bytes = 0b11010010_01101001_10010100_10010010
表現(xiàn)形式
Java 平臺(tái)會(huì)把數(shù)值作為 JVM 基礎(chǔ)類(lèi)型來(lái)物理存儲(chǔ)宰闰。除非是一個(gè)可為空的數(shù)值引用(例如 Int?
)或者有泛型引入。如果是后者簿透,數(shù)值會(huì)裝箱移袍。
注意:裝箱后的數(shù)值不會(huì)保持 identity:
val a: Int = 10000
print(a === a) // Prints 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA === anotherBoxedA) // !!!Prints 'false`!!!
但是仍然會(huì)有相等性:
val a: Int = 10000
print(a == a) // Prints 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA == anotherBoxedA) // Prints 'true'
顯示轉(zhuǎn)換
由于不同的表現(xiàn)形式,小類(lèi)型并非大類(lèi)型的子類(lèi)型老充。如果是的話(huà)葡盗,可能會(huì)帶來(lái)如下麻煩:
// Hypothetical code, does not actually compile:
val a: Int? = 1 // A boxed Int (java.lang.Integer)
val b: Long? = a // implicit conversion yields a boxed Long (java.long.Long)
print(a == b) // Surprise! This prints "false" as Long's equals() check for other part to be Long as well
所以,不只是身份(identity)蚂维,連相等性(equality)也會(huì)靜默丟失戳粒。
因此,小類(lèi)型不會(huì)隱式轉(zhuǎn)換成大類(lèi)型虫啥。這就意味著:不通過(guò)顯示轉(zhuǎn)換蔚约,我們無(wú)法把一個(gè) Byte
賦值給 Int
。
val b: Byte = 1 // OK, literals are checked statically
val i: Int = b // ERROR
通過(guò)顯示轉(zhuǎn)換可以“拓寬(widen)”數(shù)值涂籽。
val i: Int = b.toInt() // OK: explicitly widened
每個(gè)數(shù)值類(lèi)型都支持如下轉(zhuǎn)換:
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
缺少隱式轉(zhuǎn)換并不會(huì)引起注意苹祟,因?yàn)橥ㄟ^(guò)上下文可以推導(dǎo)出類(lèi)型,并且算術(shù)操作符也有支持類(lèi)型轉(zhuǎn)換的重載评雌,例如:
val l = 1L + 3 // Long + Int => Long
運(yùn)算
Kotlin 支持?jǐn)?shù)值的標(biāo)準(zhǔn)算術(shù)運(yùn)算树枫,這些運(yùn)算被聲明為相應(yīng)類(lèi)的成員(但是編譯器會(huì)把函數(shù)調(diào)用優(yōu)化成相應(yīng)的指令)。參考操作符重載景东。
位運(yùn)算操作符也沒(méi)有特殊之處砂轻,他們也只是支持中綴調(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)
- 無(wú)符號(hào)右移(Java 的>>>
) -
and(bits)
- 位的與運(yùn)算 -
or(bits)
- 位的或運(yùn)算 -
xor(bits)
- 位的異或運(yùn)算 -
inv(bits)
- 位的非運(yùn)算
浮點(diǎn)數(shù)比較
本節(jié)所要討論的浮點(diǎn)數(shù)運(yùn)算符有:
- 相等檢查:
a == b
和a != b
- 比較操作符:
a < b
斤吐,a > b
搔涝,a <= b
,a >=b
- 范圍初始化和范圍檢查:
a..b
厨喂,x in a..b
,x !in a..b
當(dāng)操作數(shù) a
和 b
靜態(tài)已知為類(lèi)型 Float
或 Double
庄呈,以及它們對(duì)應(yīng)的可空類(lèi)型(得出方式包括:聲明蜕煌、推斷或者智能轉(zhuǎn)換),數(shù)值的運(yùn)算以及它們形成的范圍(range)遵守 IEEE 754 制定的浮動(dòng)點(diǎn)數(shù)運(yùn)算規(guī)范诬留。
但是為了支持通用的使用場(chǎng)景以及提供完整的排序斜纪,當(dāng)操作數(shù)不是浮點(diǎn)數(shù)的靜態(tài)類(lèi)型(如 Any
、Comparable<...>
文兑,類(lèi)型參數(shù))時(shí)盒刚,運(yùn)算操作會(huì)使用 Float
和 Double
的 equals
和 compareTo
實(shí)現(xiàn),這會(huì)導(dǎo)致異與標(biāo)準(zhǔn)彩届,因此:
-
NaN
等于它自己 -
NaN
大與所有其他元素伪冰,包括POSITIVE_INFINITY
-
-0.0
小于0.0
字符
Char
表示字符,不能直接用作數(shù)值:
fun check(c: Char) {
if (c == 1) { // ERROR: incompatible types
// ...
}
}
字符用單引號(hào)來(lái)表示:'1'
樟蠕。特殊字符可以使用反斜杠來(lái)轉(zhuǎn)義贮聂。
特殊字符可以用反斜杠轉(zhuǎn)義。支持的轉(zhuǎn)義序列有:\t
寨辩、\b
吓懈、\n
、\r
靡狞、\'
耻警、\"
、\\
甸怕、\$
甘穿。如果要編譯其他字符,可以使用 Unicode 轉(zhuǎn)義序列語(yǔ)法:\uFF00
梢杭。
我們可以顯示地把一個(gè)字符轉(zhuǎn)換成一個(gè) Int
數(shù)值:
fun decimalDigitValue(c: Char): Int {
if (c !in '0'..'9')
throw IllegalArgumentException("Out of range")
return c.toInt() - '0'.toInt() // Explicit conversions to numbers
}
就像數(shù)值那樣温兼,字符的空引用也會(huì)自動(dòng)裝箱。裝箱操作不會(huì)保留字符的身份(identity)武契。
布爾型
Boolean
表示布爾型募判,有兩個(gè)值:true
和 false
。
布爾的可空引用會(huì)自動(dòng)裝箱咒唆。
內(nèi)置操作符包括:
-
||
- lazy disjunction -
&&
- lazy conjunction -
!
- negation
數(shù)組
Kotlin 用類(lèi) Array
來(lái)表示數(shù)組届垫,有 get
和 set
函數(shù)(利用操作符重載的約定可轉(zhuǎn)換成 []
操作),還有 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>
// ...
}
使用庫(kù)函數(shù) arrayOf()
并傳入元素值可以創(chuàng)建一個(gè)數(shù)組:arrayOf(1, 2, 3)
創(chuàng)建了 [1, 2, 3]装处。另外,arrayOfNulls()
可以創(chuàng)建一個(gè)所有元素都是 null 的數(shù)組浸船。
另一種創(chuàng)建方式是調(diào)用 Array
的構(gòu)造函數(shù),傳入數(shù)組大小和一個(gè)根據(jù)下標(biāo)返回初始值的函數(shù):
// Creates an Array<String> with values ["0", "1", "4", "9", "16"]
val asc = Array(5, { i -> (i * i).toString() })
上面已經(jīng)說(shuō)過(guò),[]
操作等價(jià)于調(diào)用成員函數(shù) get()
和 set()
罗售。
注意:與 Java 不同伶棒,Kotlin 的數(shù)組是不可變的(invariant)。這就意味著 Kotlin 不允許我們把 Array<String>
賦給 Array<Any>
项戴,這樣能避免運(yùn)行時(shí)的失斝伟铩(但是能用 Array<out Any>
,可參考類(lèi)型映射)周叮。
Kotlin 也有特定的類(lèi)用于表示基礎(chǔ)類(lèi)型數(shù)組(沒(méi)有裝箱的開(kāi)銷(xiāo)):ByteArray
辩撑、ShortArray
、IntArray
等仿耽。這幾個(gè)類(lèi)和 Array
沒(méi)有直接的繼承關(guān)系合冀,但是他們有同樣的方法和屬性。每個(gè)類(lèi)型都有相應(yīng)的工廠(chǎng)函數(shù):
val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]
字符串
字符串由 String
表示项贺。字符串是不可變的君躺。字符串的元素可通過(guò)下標(biāo)訪(fǎng)問(wèn):s[i]
。字符串可通過(guò) for
循環(huán)遍歷:
for (c in str) {
println(c)
}
字符串字面值
Kotlin 支持兩種類(lèi)型的字符串字面值:包含轉(zhuǎn)義字符的轉(zhuǎn)義字符串和包含換行和任意文本的純字符串开缎。轉(zhuǎn)義字符串跟 Java 類(lèi)似:
val s = "Hello, world\n"
轉(zhuǎn)義遵守約定俗成的方式(利用 \)棕叫。上面的字符那一章節(jié)已經(jīng)列出了所有支持的轉(zhuǎn)義序列。
純字符串通過(guò)三個(gè)引號(hào)("""
)來(lái)界定奕删,它不會(huì)包含轉(zhuǎn)義而且能夠包含換行和任意字符:
val text = """
for (c in "foo")
print(c)
"""
可以通過(guò) trimMargin()
去除開(kāi)頭的空字符:
val text = """
|Tell me and I forget
|Teach me and I remember.
|Involve me and I learn.
|(Benjamin Franklin)
""".trimMargin()
默認(rèn)情況下俺泣,|
用作 margin 前綴,但是也可以使用其他字符作為參數(shù)傳給 trimMargin
完残,例如 trimMargin(">")
伏钠。
字符串模板
字符串可以包含模板表達(dá)式,例如谨设,可被求值的代碼片段熟掂,求值結(jié)果可以連接到字符串中。模板表達(dá)式以美元符號(hào)($)開(kāi)始铝宵,由一個(gè)簡(jiǎn)單的名稱(chēng)組成:
val i = 10
val s = "i = $i" // evaluates to "i = 10"
或者是大括號(hào)內(nèi)的任意表達(dá)式:
val s = "abc"
val str = "$s.length is ${s.length}" // evaluates to "abc.length is 3"
模板可用于純字符串和轉(zhuǎn)義后的字符串內(nèi)打掘。如果要在純字符串(不支持轉(zhuǎn)義)中展示 $
符號(hào),可以使用如下語(yǔ)法:
val price = """
${'$'}9.99
"""