Kotlin 教程之「基礎(chǔ)類型」

Kotlin 中,我們可以調(diào)用任何變量的成員函數(shù)和屬性,從這個角度來說,一切皆對象。某些類型可以有特殊的內(nèi)部表現(xiàn) - 例如,數(shù)字、字符和布爾型在運(yùn)行時可以表現(xiàn)為基礎(chǔ)類型(primitive types),但是對用戶來說,他們看上去就是是普通的類。這一章節(jié)主要描述 Kotlin 的基本類型:數(shù)字、字符、布爾、數(shù)組和字符串。

數(shù)值

Kotlin 處理數(shù)字的方式與 Java 類似,但不是完全一致。例如,數(shù)值沒有隱式的拓寬轉(zhuǎn)換(implicit widening conversions),某些情況下,字面意思也會稍有不同。

Kotlin 提供了如下內(nèi)置類型來表示數(shù)值(接近 Java):

類型 位寬
Double 64
Float 32
Long 64
Int 32
Short 16
Byte 8

注意:字符不是一種數(shù)值。

字面常量

整形值的字面常量有如下形式:

  • 十進(jìn)制:123
    • 長整型用 L 做標(biāo)記:123L
  • 十六進(jìn)制:0x0F
  • 二進(jìn)制:0b00001011

注意:不支持八進(jìn)制。

浮點數(shù)也支持約定的標(biāo)記:

  • double 類型:123.5123.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_D5_5E
val bytes = 0b11010010_01101001_10010100_10010010

表現(xiàn)形式

Java 平臺會把數(shù)值作為 JVM 基礎(chǔ)類型來物理存儲。除非是一個可為空的數(shù)值引用(例如 Int?)或者有泛型引入。如果是后者,數(shù)值會裝箱。

注意:裝箱后的數(shù)值不會保持 identity

val a: Int = 10000
print(a === a) // Prints 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA === anotherBoxedA) // !!!Prints 'false`!!!

但是仍然會有相等性:

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)形式,小類型并非大類型的子類型。如果是的話,可能會帶來如下麻煩:

// 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)也會靜默丟失。

因此,小類型不會隱式轉(zhuǎn)換成大類型。這就意味著:不通過顯示轉(zhuǎn)換,我們無法把一個 Byte 賦值給 Int。

val b: Byte = 1 // OK, literals are checked statically
val i: Int = b // ERROR

通過顯示轉(zhuǎn)換可以“拓寬(widen)”數(shù)值。

val i: Int = b.toInt()  // OK: explicitly widened

每個數(shù)值類型都支持如下轉(zhuǎn)換:

  • toByte(): Byte
  • toShort(): Short
  • toInt(): Int
  • toLong(): Long
  • toFloat(): Float
  • toDouble(): Double
  • toChar(): Char

缺少隱式轉(zhuǎn)換并不會引起注意,因為通過上下文可以推導(dǎo)出類型,并且算術(shù)操作符也有支持類型轉(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)類的成員(但是編譯器會把函數(shù)調(diào)用優(yōu)化成相應(yīng)的指令)。參考操作符重載

位運(yùn)算操作符也沒有特殊之處,他們也只是支持中綴調(diào)用的命名函數(shù),例如:

val x = (1 shl 2) and 0x000FF000

如下是位運(yùn)算操作符的完整列表(只用于 IntLong):

  • shl(bits) - 有符號左移(Java 的 <<
  • shr(bits) - 有符號右移(Java 的 >>
  • ushr(bits) - 無符號右移(Java 的 >>>
  • and(bits) - 位的與運(yùn)算
  • or(bits) - 位的或運(yùn)算
  • xor(bits) - 位的異或運(yùn)算
  • inv(bits) - 位的非運(yùn)算

浮點數(shù)比較

本節(jié)所要討論的浮點數(shù)運(yùn)算符有:

  • 相等檢查:a == ba != b
  • 比較操作符:a < b,a > b,a <= b, a >=b
  • 范圍初始化和范圍檢查:a..bx in a..b,x !in a..b

當(dāng)操作數(shù) ab 靜態(tài)已知為類型 FloatDouble,以及它們對應(yīng)的可空類型(得出方式包括:聲明、推斷或者智能轉(zhuǎn)換),數(shù)值的運(yùn)算以及它們形成的范圍(range)遵守 IEEE 754 制定的浮動點數(shù)運(yùn)算規(guī)范。

但是為了支持通用的使用場景以及提供完整的排序,當(dāng)操作數(shù)不是浮點數(shù)的靜態(tài)類型(如 Any、Comparable<...>,類型參數(shù))時,運(yùn)算操作會使用 FloatDoubleequalscompareTo 實現(xiàn),這會導(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
        // ...
    }
}

字符用單引號來表示:'1'。特殊字符可以使用反斜杠來轉(zhuǎn)義。

特殊字符可以用反斜杠轉(zhuǎn)義。支持的轉(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() // Explicit conversions to numbers
}

就像數(shù)值那樣,字符的空引用也會自動裝箱。裝箱操作不會保留字符的身份(identity)。

布爾型

Boolean 表示布爾型,有兩個值:truefalse。

布爾的可空引用會自動裝箱。

內(nèi)置操作符包括:

  • || - lazy disjunction
  • && - lazy conjunction
  • ! - negation

數(shù)組

Kotlin 用類 Array 來表示數(shù)組,有 getset 函數(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>
    // ...
}

使用庫函數(shù) arrayOf() 并傳入元素值可以創(chuàng)建一個數(shù)組:arrayOf(1, 2, 3) 創(chuàng)建了 [1, 2, 3]。另外,arrayOfNulls() 可以創(chuàng)建一個所有元素都是 null 的數(shù)組。

另一種創(chuàng)建方式是調(diào)用 Array 的構(gòu)造函數(shù),傳入數(shù)組大小和一個根據(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)說過,[] 操作等價于調(diào)用成員函數(shù) get()set()。

注意:與 Java 不同,Kotlin 的數(shù)組是不可變的(invariant)。這就意味著 Kotlin 不允許我們把 Array<String> 賦給 Array<Any>,這樣能避免運(yùn)行時的失?。ǖ悄苡?Array<out Any>,可參考類型映射)。

Kotlin 也有特定的類用于表示基礎(chǔ)類型數(shù)組(沒有裝箱的開銷):ByteArray、ShortArray、IntArray 等。這幾個類和 Array 沒有直接的繼承關(guān)系,但是他們有同樣的方法和屬性。每個類型都有相應(yīng)的工廠函數(shù):

val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]

字符串

字符串由 String 表示。字符串是不可變的。字符串的元素可通過下標(biāo)訪問:s[i]。字符串可通過 for 循環(huán)遍歷:

for (c in str) {
    println(c)
}

字符串字面值

Kotlin 支持兩種類型的字符串字面值:包含轉(zhuǎn)義字符的轉(zhuǎn)義字符串和包含換行和任意文本的純字符串。轉(zhuǎn)義字符串跟 Java 類似:

val s = "Hello, world\n"

轉(zhuǎn)義遵守約定俗成的方式(利用 \)。上面的字符那一章節(jié)已經(jīng)列出了所有支持的轉(zhuǎn)義序列。

純字符串通過三個引號(""")來界定,它不會包含轉(zhuǎn)義而且能夠包含換行和任意字符:

val text = """
    for (c in "foo")
        print(c)
"""

可以通過 trimMargin() 去除開頭的空字符:

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á)式以美元符號($)開始,由一個簡單的名稱組成:

val i = 10
val s = "i = $i" // evaluates to "i = 10"

或者是大括號內(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)義)中展示 $ 符號,可以使用如下語法:

val price = """
${'$'}9.99
"""
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容