# 基本類型
Kotlin 中的理念是一切皆為對象,我們可以在任何變量上調用函數和屬性。一些類型是內置的,因為他們的實現經過了優化,但在用戶看來它們與普通類一樣。這個章節中我們描述一下大多數這些類型:數字、字符、布爾和數組。
## 數字
從某種程度上說 Kotlin 對數字的操作與 Java 很接近,但并不盡相同。比如說,沒有為數字作出隱含地拓寬轉換,而且在某些情況下略微有些不一樣。
Kotlin 提供下列內置的類型來代表數字(和 Java 很接近):
| 類型 | 位寬 |
|--------|----------|
| Double | 64 |
| Float | 32 |
| Long | 64 |
| Int | 32 |
| Short | 16 |
| Byte | 8 |
注意在 Kotlin 中字符不是數字。
### 進制數
整數值中如下幾種進制:
* 10 進制: `123`
* 長整型標記為大寫的 `L`: `123L`
* 16 進制數: `0x0F`
* 2 進制數: `0b00001011`
注意:不支持 8 進制數。
Kotlin 同樣支持以浮點表示的數字:
* 默認為雙精度: `123.5`,`123.5e10`
* 單精度以 `f` 和 `F` 標記: `123.5f`
## 數字中的下劃線(1.1以上版本)
你可以在數字常量中使用下劃線以增加可讀性:
``` kotlin
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 平臺,數字是作為 JVM 原始類型進行物理存儲,除非我們需要引用一個可空的數字(例如:`Int?`)或者更復雜。下面的案例中數字會被包裝。
注意包裝的數字不會保持一致:
``` kotlin
val a: Int = 10000
print(a === a) // 將打印 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA === anotherBoxedA) // !!!將打印 'false'!!!
```
其它時候會保持相等:
``` kotlin
val a: Int = 10000
print(a == a) // 將打印 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA == anotherBoxedA) // 將打印 'true'
```
### 明確的轉換
因客觀的不同,較小的類型并不是較大類型的子類型。如果是的話,我們會在下面的排序中遇到麻煩:
``` kotlin
// 假想代碼,實際上無法編譯:
val a: Int? = 1 // 被包裝的 Int(java.lang.Integer)
val b: Long? = a // 隱含地產生包裝過的 Long(java.lang.Long)
print(a == b) // 驚喜!打印出的“false”和 Long 與其它的 Long 所作的的 equals() 檢查一樣。
```
因此不僅僅是不一致,甚至等式也會靜默地丟失所在的空間。
正如結果所示,較小的類型**不會**隱含地轉換為較大的類型。意即我們不能在沒有明確的轉換時把 `Byte` 類型賦值給一個 `Int` 類型
``` kotlin
val b: Byte = 1 // OK,可以通過靜態檢查
val i: Int = b // 錯誤
```
我們可以使用明確地轉換來拓展數字
``` kotlin
val i: Int = b.toInt() // OK: 明確地擴展
```
每一個數字都支持下列轉換:
* `toByte(): Byte`
* `toShort(): Short`
* `toInt(): Int`
* `toLong(): Long`
* `toFloat(): Float`
* `toDouble(): Double`
* `toChar(): Char`
缺乏隱式轉換這一點很少被注意,因為類型會從上下文中進行推斷,并且算術運算中會用適當的轉換進行重載。例如
``` kotlin
val l = 1L + 3 // Long + Int => Long
```
### 操作
Kotlin 支持聲明為適當類成聽見沒的標準數字算術運算(但編譯器優化這些調用為一致的命令)。查閱 [操作重載](operator-overloading.html)
如同位操作,他們沒有特別的字符,但函數名可以以中綴的形式調用。例如:
``` kotlin
val x = (1 shl 2) and 0x000FF000
```
這里列出完整的位操作列表(只對 `Int` 和 `Long` 有效):
* `shl(bits)` – 帶符號左移(Java 的 `<<`)
* `shr(bits)` – 帶符號右移(Java 的 `>>`)
* `ushr(bits)` – 無符號右移(Java's `>>>`)
* `and(bits)` – 按位與
* `or(bits)` – 按位或
* `xor(bits)` – 按位異或
* `inv()` – 按位取反
## 字符
字符表現為 `Char` 類型。不能把它們直接視為數字
``` kotlin
fun check(c: Char) {
if (c == 1) { // 錯誤:類型沖突
// ...
}
}
```
字符以一對單引號包圍。特殊字符可以用反斜杠轉義。這些轉義序列都被支持:`\t`, `\b`, `\n`, `\r`, `\'`, `\"`, `\\` 和 `\$`。要編碼其它字符,使用 Unicode 轉義序列語法:`'\uFF00'`。
我們可以明確地轉換一個字符為一個 `Int` 數字:
``` kotlin
fun decimalDigitValue(c: Char): Int {
if (c !in '0'..'9')
throw IllegalArgumentException("Out of range")
return c.toInt() - '0'.toInt() // 明確地轉換為數字
}
```
類似數字,字符在需要空引用時會被包裝。包裝操作不保證一致性。
## 布爾
類型 `Boolean` 表達布爾值,其有兩種值:`true` 和 `false`。
布爾值如果需要空引用也會被包裝。
布爾值的內置操作包括
* `||` – 延遲分離
* `&&` – 延遲聯合
* `!` - 否定
## 數組
數組在 Kotlin 中表現為 `Array` 類,它擁有 `get` 和 `set` 函數(通過操作重載轉換為 `[]`),還有 `size` 屬性,還有其它幾個非常有用的成員函數:
``` kotlin
class Array<T> private constructor() {
val size: Int
fun get(index: Int): T
fun set(index: Int, value: T): Unit
fun iterator(): Iterator<T>
// ...
}
```
要創建一個數組,我們可以使用一個庫函數 `arrayOf()` 并傳遞一些項值給它。因此 `arrayOf(1, 2, 3)` 創建一個數組 [1, 2, 3]。另外,庫函數 `arrayOfNulls()` 可以用來創建一個填滿 null 元素的給定大小的數組。
另外的選擇是使用一個工廠函數給出數組大小,而且這個函數會返回每個給定索引的數組元素初始值:
``` kotlin
// 創建一個 Array<String>,值為 ["0", "1", "4", "9", "16"]
val asc = Array(5, { i -> (i * i).toString() })
```
如上文所述,`[]` 操作代表要調用成員函數 `get()` 和 `set()`。
注意:與 Java 不一樣的是,Kotlin 中的數組是不可變的。這個意思是 Kotlin 不讓我們把一個 `Array<String>` 賦值給一個 `Array<Any>`,會適當的終止運行時來阻止(但你可以用 `Array<out Any>`,查看[類型投射](generics.html#type-projections))。
Kotlin 還有專門的類來表示原始類型的數組而沒有額外的包裝開銷:`ByteArray`、 `ShortArray`、 `IntArray` 等等。這些類與 `Array` 類之間沒有繼承關系,但他們有相同的 set 方法和屬性。它們每一個都有一個相同的工廠函數:
``` kotlin
val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]
```
## 字符串
字符串通過 `String` 類型來表示。字符串是不可變的。一個字符串的元素是可以通過索引操作訪問的一些字符:`s[i]`。一個客串可以與 `for` 循環迭代:
``` kotlin
for (c in str) {
println(c)
}
```
### 字符串值
Kotlin 有兩類字符串值:可以有轉義字符的轉義字符串和可以包含新行與任意文本的原始字符串。一個轉義字符串非常像 Java 字符串:
``` kotlin
val s = "Hello, world!\n"
```
轉義和傳統方法一樣與一個反斜杠一起。查看上面的[字符](#characters)章節中列出的所支持的轉義序列。
一個原始字符串通過一對三個一組的引號來界定(`"""`),不飲食轉義并且可以包含新行和其它字符:
``` kotlin
val text = """
for (c in "foo")
print(c)
"""
```
### 字符串模板
字符串可以包含模板表達式,意即代碼片斷會被評估并決定什么結果會聯結進字符串。一個模板表達式以一個美元符號開始($)并跟一個已有的命名名稱:
``` kotlin
val i = 10
val s = "i = $i" // evaluates to "i = 10"
```
或者是花括號內的任意表達式:
``` kotlin
val s = "abc"
val str = "$s.length is ${s.length}" // evaluates to "abc.length is 3"
```
原始字符串和轉義字符串都支持模板。如果你需要在一個原始字符串中表示一個 `$` 字符值(不支持反斜杠轉義),你可以使用下面的語法:
``` kotlin
val price = """
${'$'}9.99
"""
```