# 基本类型
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
"""
```