本文为翻译文本,原文来自Kotlin reference,翻译参考了Kotlin 中文站
Kotlin中,一切皆对象,也就是说我们可以对任意变量访问其对应的方法和属性。有一些类型有特殊的内部表示方法,但对于用户来说他们和普通的类没有区别,例如数值,字符和布尔值能够在运行时用原始值表示。这一部分我们将介绍Kotlin中的基本数据类型:数值、字符、布尔值、数组和字符串。
数值类型
Kotlin处理数值的方法和Java非常类似,但并不完全相同。例如,Kotlin中没有对数值类型的隐式类型提升,在某些情况下对字面数值常量的处理也有一点点不同。
Kotlin提供如下内建数值类型(这个Java很像):
Type | Bit width |
---|---|
Double | 64 |
Float | 32 |
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
特别地,Kotlin中的字符不是数值类型。
字面常量
以下是一些 Int 字面常量的例子:
- 十进制:
123
- Long类型需要用
L
标识:123L
- Long类型需要用
- 十六进制:
0x0F
- 二进制:
0b00001011
注意,Kotlin不支持八进制字面整型常量。
Kotlin还支持浮点数标识:
- 默认的Double类型:
123.5
,123.5e10
- 用
f
或F
标识的 Float 类型:123.5f
在数值常量中使用下划线 (Kotlin 1.1)
你可以使用下划线增强数值常量的可读性:
val onoMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010111_01011010
数值的表示
在Java平台上,数值类型物理存储为JVM的原生类型,除非我们需要一个可能为null的(nullable)数值引用(例如:Int?
)或者涉及到泛型。当不是原生类型时,数值会被包装(boxed)。
注意,包装后的数值不保证同一性(identity):
val a: Int = 10000
print(a === a) // 此处输出 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA === anotherBoxedA) // !!!此处输出 'false'!!!
但另一方面,包装会保留值的相等性质:
val a: Int = 10000
print(a == a) // Prints 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA == anotherBoxedA) // Prints 'true'
显式类型转换
由于表示方法的不同,较小类型不是较大类型的子类型。如果是的话就会有如下的问题:
// 假想代码,无法通过编译
val a: Int? = 1 // A boxed Int (java.lang.Integer)
val b: Long? = a // implicit conversion yields a boxed Long (java.lang.Long)
print(a == b) // 由于Java中Long类的equals()函数还会检查参数是否为Long类型,这里输出"false",很令人疑惑。
这样一来,不仅是同一性,相等性也会悄然丢失。
基于以上原因,较小类型不会隐式转化为较大类型。这就意味着,在没有显式类型转化的前提下,我们不能将一个 Byte
类型的值赋值给一个 Int
型变量
val b: Byte = 1 // OK,字面常量是静态检查的
val i: Int = b // ERROR
但是,我们可以显示执行类型提升
val i:Int = b.toInt() //OK,显式类型转化
所有的数值类型都支持以下类型转化方法:
—
toByte(): Byte
—
toShort(): Short
—
toInt(): Int
—
toLong(): Long
—
toFloat(): Float
—
toDouble(): Double
- —
toChar(): Char
省略隐式类型转化很少被注意到,因为这种转化可以通过上下问推断。重载的算数运算符能够实现适当的类型转化,例如:
val l = 1L + 3 // Long + Int => Long
数值运算
Kotlin支持标准的数值运算,他们被定义为相应类的成员函数(但是编译器会将他们优化为对应的运算符),参考运算符重载。
对于位运算虽然没有相应的运算符,但是有相应的可以用中缀形式调用的方法。例如:
val x = (1 shl 2) and 0x000FF000
以下是位完整的位操作列表:
—
shl(bits)
–有符号左移,等价与Java中的<<
—
shr(bits)
–有符号右移,等价与Java中的>>
—
ushr(bits)
–无符号右移,等价与Java中的>>>
—
and(bits)
–按位与—
or(bits)
–按位或—
xor(bits)
–按位异或- —
inv()
–按位取反
左移操作有无符号都一样,符号位不保留,低位补零。
字符类型
字符使用 Char
表示,Kotlin中不能将字符直接当作数值类型对待。
fun check(c: Char) {
if (c == 1) { // ERROR: incompatible types
// ...
}
}
字符字面常量用单引号标识:'1'
。特殊字符使用反斜杠转义。Kotlin支持以下转义字符:\t
, \b
, \n
, \r
, \'
, \"
, \\
和 \$
。如果需要编码其他字符则需要使用Unicode转义语法:\uFF00
。
我们可以使用 toInt()
将字符显式转化为 Int
类型数值:
fun decimalDigitalValue(c: Char): Int {
if(c !in '0'..'9') {
throw IllegalArgumentException("Out of range")
}
return c.toInt() - '0'.toInt() // 显式转化为数值类型
}
布尔类型
关键字 Boolean
表示布尔类型。布尔类型具有 true
和 false
两个值。
如果布尔类型值是nullable,那么就会被包装。
布尔类型的内建操作包括:
- —
||
或逻辑。惰性判断(如果前者为true
结果为true
,不判断后者) - —
&&
与逻辑。惰性判断(如果前者为false
结果为false
,不判断后者) - —
!
非逻辑
数组类型
Kotlin中数组使用包含 get
和 set
方法(操作符 []
会被重载为调用这些方法),size
属性,以及其他方法的 Array
类表示:
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>
// ...
}
我们可以使用库函数 arrayOf()
并传递元素值来创建数组。例如,arrayOf()
创建数组 [1, 2, 3]。另外,arrayOfNulls()
库函数可以用来创建一个包含给定数量的null元素的数组。
另一个创建数组的方法是使用工厂方法(factory function)。该工厂方法接受两个参数,一个用于指定数组大小;另一个参数为函数,该函数能够根据数组下标计算对应的元素值。
// Creates an Array<String> with values ["0", "1", "4", "9", "16"]
val asc = Array(5, { i -> (i * i).toString() })
正如我们上面提到的,运算符 []
表示调用 get()
和 set()
方法。
注意:不同于Java,在Kotlin中的数组是不型变的。这就意味着我们不能将一个 Array<String>
赋值给 一个 Array<Any>
类型变量(在Java中我们可以将Integer[]
对象赋值给 Object[]
变量),这避免了某些运行时错误(但我们可以使用 Array<out Any>
, 参考类型投影)。
Kotlin还提供 ByteArray
, ShortArray
, IntArray
等,表示内建类型数组的特例化数组类,这能够避免包装开销。虽然这些类和 Array
类没有继承关系,但仍然拥有 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有两种字符串字面常量:包含转义字符的转义字符串;包含换行以及其他任意文本的原始字符串(raw string)。转义字符串和Java中的字符串常量非常类似:
val s = "Hello World!\n"
转义通过传统的方法实现,即反斜杠。参考前文中Kotlin支持的转义字符列表。
原始字符串(raw string)使用三引用("""
)表示,它不包含任何转义字符,并且能够包含换行和任意其他的字符:
val test = """
for(c in "foo")
print(c)
"""
前置空格可以使用 trimMargin()
方法去除:
val test = """
|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" // evaluates to "i = 10"
这个在 $
后紧跟一个由大括号包裹的表达式:
val s = "abc"
val str = "$s.length is ${ s.length }" // evaluates to "abc.length is 3"
模版表达式支持转义字符串也支持原始字符串,如果你需要在原始字符串(不支持反斜杠转义)中输出一个 $
字符,那么可以使用一下的语法:
val price = """
${ '$' }9.99
"""
TODO list:
完善文中链接