@cxm-2016
2016-11-21T10:57:23.000000Z
字数 4491
阅读 2892
Kotlin
版本:4
翻译:李颖
转载自:Kotlin Reference
在 Kotlin 中, 一切都是对象, 这就意味着, 我们可以对任何变量访问它的成员函数和属性. 有些数据类型是内建的(built-in), 因为对它们的实现进行了优化, 但对于使用者来说内建类型与普通类没有区别. 本节我们将介绍大部分内建类型: 数值, 字符, 布尔值, 以及数组.
Kotlin 处理数值的方式与 Java 类似, 但并不完全相同. 比如, 对数值不会隐式地扩大其值范围, 而且在某些情况下, 数值型的字面值(literal)也与 Java 存在轻微的不同.
Kotlin 提供了以下内建类型来表达数值(与 Java 类似):
Type | Bit width |
---|---|
Double | 64 |
Float | 32 |
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
注:在Kotlin中字符不再表示为数字
对于整数值, 有以下几种类型的字面值常数:
注意: 不支持8进制数的字面值.
Kotlin 还支持传统的浮点数值表达方式:
在 Java 平台中, 数值的物理存储使用 JVM 的基本类型来实现, 但当我们需要表达一个可为 null 的数值引用时(比如. Int?
), 或者涉及到泛型时, 我们就不能使用基本类型了. 这种情况下数值会被装箱(box)为数值对象.
注意, 数值对象的装箱(box)并不保持对象的同一性(identity):
val a: Int = 10000
print(a === a) // 打印结果为 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA === anotherBoxedA) // !!!打印结果为 'false'!!!
但是, 装箱(box)会保持对象内容相等(equality):
val a: Int = 10000
print(a == a) // 打印结果为 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA == anotherBoxedA) // 打印结果为 'true'
由于数据类型内部表达方式的差异, 较小的数据类型不会被看作较大数据类型的子类型(subtype). 如果小数据类型是大数据类型的子类型, 那么我们将会遇到以下问题:
// 以下为假想代码, 实际上是无法编译的:
val a: Int? = 1 // 装箱后的 Int (java.lang.Integer)
val b: Long? = a // 这里进行隐式类型转换, 产生一个装箱后的 Long (java.lang.Long)
print(a == b) // 结果与你期望的相反! 这句代码打印的结果将是 "false", 因为 Long 的 equals() 方法会检查比较对象, 要求对方也是一个 Long 对象
这样, 不仅不能保持同一性(identity), 而且还在所有发生隐式类型转换的地方, 保持内容相等(equality)的能力也静悄悄地消失了.
由于存在以上问题, Kotlin 不会将较小的数据类型隐式地转换为较大的数据类型. 也就是说, 如果不进行显式类型转换, 我们就不能将一个 Byte 类型值赋给一个 Int 类型的变量.
val b: Byte = 1 // 这是 OK 的, 因为编译器会对字面值进行静态检查
val i: Int = b // 这是错误的
我们可以使用显式类型转换, 来将数值变为更大的类型
val i: Int = b.toInt() // 这是 OK 的: 我们明确地扩大了数值的类型
所有的数值类型都支持以下类型转换方法:
Kotlin 语言中缺少了隐式类型转换的能力, 这个问题其实很少会引起使用者的注意, 因为类型可以通过代码上下文自动推断出来, 而且数学运算符都进行了重载(overload), 可以适应各种数值类型的参数, 比如
val l = 1L + 3 // Long 类型 + Int 类型, 结果为 Long 类型
Kotlin 对数值类型支持标准的数学运算符(operation), 这些运算符都被定义为相应的数值类上的成员函数(但编译器会把对类成员函数的调用优化为对应的运算指令).
对于位运算符, 没有使用特别的字符来表示, 而只是有名称的普通函数, 但调用这些函数时, 可以将函数名放在运算数的中间(即中缀表示法), 比如:
val x = (1 shl 2) and 0x000FF000
以下是位运算符的完整列表(只适用于 Int 类型和 Long 类型):
字符使用 Char
类型表达. 字符不能直接当作数值使用
fun check(c: Char) {
if (c == 1) { // 错误: 类型不兼容
// ...
}
}
字符的字面值(literal)使用单引号表达: '1'
. 特殊字符使用反斜线转义表达. Kotlin 支持的转义字符包括: \t
, \b
, \n
, \r
, \'
, \"
, \\
以及 \$
. 其他任何字符, 都可以使用 Unicode 转义表达方式: '\uFF00'
.
我们可以将字符显式地转换为 Int 型数值:
fun decimalDigitValue(c: Char): Int {
if (c !in '0'..'9')
throw IllegalArgumentException("Out of range")
return c.toInt() - '0'.toInt() // 显式转换为数值
}
与数值型一样, 当需要一个可为 null 的字符引用时, 字符会被装箱(box)为对象. 装箱操作不保持对象的同一性(identity).
Boolean
类型用来表示布尔值, 有两个可能的值: true 和 false.
当需要一个可为 null 的布尔值引用时, 布尔值也会被装箱(box).
布尔值的内建运算符有
||
– 或运算(会进行短路计算)&&
– 与运算(会进行短路计算)!
- 非运算Kotlin 中的数组通过 Array 类表达, 这个类拥有 get 和 set 函数(这些函数通过运算符重载转换为 [] 运算符), 此外还有 size 属性, 以及其他一些有用的成员函数:
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 值.
另一种方案是使用一个工厂函数, 第一个参数为数组大小, 第二个参数是另一个函数, 这个函数接受数组元素下标作为自己的输入参数, 然后返回这个下标对应的数组元素的初始值:
// 创建一个 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>
).
Kotlin 中也有专门的类来表达基本数据类型的数组: ByteArray
, ShortArray
, IntArray
等等, 这些数组可以避免数值对象装箱带来的性能损耗. 这些类与 Array
类之间不存在继承关系, 但它们的方法和属性是一致的. 各个基本数据类型的数组类都有对应的工厂函数:
val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]
字符串由 String
类型表示. 字符串的内容是不可变的. 字符串中的元素是字符, 可以通过下标操作符来访问: s[i]
. 可以使用 for 循环来遍历字符串:
for (c in str) {
println(c)
}
Kotlin 中存在两种字符串字面值: 一种称为转义字符串(escaped string), 其中可以包含转义字符, 另一种成为原生字符串(raw string), 其内容可以包含换行符和任意文本. 转义字符串(escaped string) 与 Java 的字符串非常类似:
val s = "Hello, world!\n"
转义字符使用通常的反斜线方式表示. 关于 Kotlin 支持的转义字符。
原生字符串(raw string)由三重引号表示("""
), 其内容不转义, 可以包含换行符和任意字符:
val text = """
for (c in "foo")
print(c)
"""
你可以使用 trimMargin() 函数来删除字符串的前导空白(leading whitespace):
val text = """
|Tell me and I forget.
|Teach me and I remember.
|Involve me and I learn.
|(Benjamin Franklin)
""".trimMargin()
默认情况下, 会使用 |
作为前导空白的标记前缀, 但你可以通过参数指定使用其它字符, 比如 trimMargin(">")
.
字符串内可以包含模板表达式, 也就是说, 可以包含一小段代码, 这段代码会被执行, 其计算结果将被拼接为字符串内容的一部分. 模板表达式以 $
符号开始, $
符号之后可以是一个简单的变量名:
val i = 10
val s = "i = $i" // 计算结果为 "i = 10"
$
符号之后也可以是任意的表达式, 由大括号括起:
val s = "abc"
val str = "$s.length is ${s.length}" // 计算结果为 "abc.length is 3"
原生字符串(raw string)和转义字符串(escaped string)内都支持模板. 由于原生字符串无法使用反斜线转义表达方式, 如果你想在字符串内表示 $
字符本身, 可以使用以下语法:
val price = """
${'$'}9.99
"""