声明变量
Kotlin是强类型语言,Kotlin要求所有的变量必须先声明后使用,声明变量时必须显式或隐式的指定变量的类型,声明变量的语法如下:
var | val 变量名 [ : 类型 ] [ = 初始值 ]
其中var声明的变量是值可变的(可以被多次赋值),val声明的变量值是不可变的(不能被重新赋值),在上面声明变量的语法格式中,程序要么通过“:类型”的形式显式的指明该变量的类型,要么为该变量指定初始值。当然程序既可以在为变量声明该变量的类型的同时为它指定初始值,但是显示指定的变量类型必须与初始值的类型保持一致。
fun main(args: Array<String>) {
//显示指定变量类型
var s: String
s = "xq"
//赋初始值,编译器通过初始值来确定变量类型
var value = 5
//显示指定变量类型并且赋初始值
val temp: String = "xy"
//val是不可变的,不能重新赋值
//temp="sq"
}
val声明的不可变的变量其实相当于常量,这意味着它的值一旦被初始化之后,将不可以被重新赋值。根据常量位置的不同可分为两种:
- 局部范围的常量:这种常量允许在声明时不指定初始值,只要在第一次使用之前指定初始值即可。
- 类的常量属性:这种常量既可以在声明时指定初始值,也可以在构造方法中指定初始值。
注意:需要指出的是,由于Kotlin程序编译的字节码必须遵守JVM规范,因此如果直接在Kotin程序中定义变量、函数,kotin将会自动生成一个名为“文件名首字母大写+Kt”的类,并将变量转换为该类的静态的getter、setter 方法(其中val声明的只有getter方法),函数则转换为该类的静态方法。
整型
与java类似,Kotlin也提供了4种整型,并且兼容了对应的java的类型。
- Byte:Byte型整数通常在内存中占8位,表示的范围是-128~127。
- Short:Short型整数通常在内存中占16位,表示的范围是-215 ~ 215 - 1。
- Int:Int型整数通常在内存中占32位,表示的范围是-231 ~ 231 - 1。
- Long:Long型整数通常在内存中占64位,表示的范围是-263 ~ 263 - 1。
如果声明变量时没有指定数据类型,而是指定它的初始值为整数,那么Kotlin会自动判断该变量的类型为Int,但是如果该值较大,超出了Int的范围,那么Kotlin将会默认它是Long型,这时如果赋值给Byte,Short,Int将会报错。
上面4种类型都是引用类型,都继承了Number型,因此他们都可以调用方法,访问属性。
fun main(args: Array<String>) {
//输出Int的最大值
println(Int.MAX_VALUE)
}
Kotlin是null安全的语言,因此Byte、Short、Int、Long都是不能接受null值,如果要存储null值就需要使用Byte?、Short?、Int?、Long?。
fun main(args: Array<String>) {
//Int型不接受null值,所以下面的代码是错的
//var notNull :Int = null
//Int型接受null值
var nullValue :Int? = null
}
Kotlin语言允许在已有数据类型后添加“?”,添加“?”后的数据类型相当于对原有类型进行了扩展,带“?”的数据类型可支持被赋予null值。此外,整数类型添加“?”后缀与不加后缀还有一个区别,普通类型的整型变量将会映射成Java的基本类型;带“?”后缀的整型变量将会映射成基本类型的包装类。举例来说,Kotin程序中Int类型的变量将会映射成Java的int基本类型,但Int?类型的变量则会自动映射成Java的Integer类型。如下所示:
fun main(args: Array<String>) {
var temp1: Int = 200
var temp2: Int = 200
println(temp1 === temp2) //基本类型比较,输出true
var temp3: Int? = 200
var temp4: Int? = 200
println(temp3 === temp4) //引用类型比较,输出false
}
上面代码中由于temp1、temp2两个变量都映射成Java的基本类型,因此,即使用“===”比较相等(该运算符要求两个变量引用同一个对象)也返回true;
但temp3、temp4两个变量都映射成Java的Integer对象,此时用temp3和temp4不再引用同一个对象,因此它们用“===”比较返回false。
注意:如果将temp3、temp4的值改为小于-128~127之间的整数,那么上面temp3和temp4用“===”比较依然是相等的,这是由Java的Integer类的特征决定的。简单解释就是Java把-128到127的数都缓存了引用这个范围内的数都会指向同一个对象,不在这个范围内的就会遵循装箱的规则了,不是同一个对象。
Kotlin的整数数值有3种表示方式:
- 十进制:最普通的整数数值就是十进制的整数。
- 二进制:以0b或0B开头的整数数值就是二进制的整数。
- 十六进制:以0x或0X开头的整数数值就是十六进制的整数,其中10~15分别以a~f(此处的a~f不区分大小写)来表示。
fun main(args: Array<String>) {
var binValue: Int = 0b100
println("binValue的值为{$binValue}")
var hexValue: Int = 0x200ff
println("hexValue的值为{$hexValue}")
}
上面代码中用到了字符串模板,也就是在字符串中嵌入${}的形式,在${}内可放入变量或表达式,Kotin会将该变量或表达式的值“嵌入”该字符串内。
为了提高数值(包括浮点型)的可读性,Kotlin允许为数值(包括浮点型)增加下画线作为分隔符。这些下画线和零并不会影响数值本身。例如如下代码:
fun main(args: Array<String>) {
var value1: Int = 100_00
var value2: Int = 1234_1234
//输出结果10000
println(value1)
//输出结果12341234
println(value2)
}
浮点型
浮点型数值可以包含小数部分,浮点型比整型的表数范围更大,可以存储比Long型更大或更小的数。Kotlin 的浮点型有两种。
- Float:表示32位的浮点型,当精度要求不高时可以使用此种类型。
- Double:表示64位的双精度浮点型,当程序要求存储很大或者高精度的浮点数时使用这种类型。
Kotlin的浮点数有两种表示形式。
- 十进制数形式:这种形式就是简单的浮点数,例如5.12、512.0、 0.512 等。浮点数必须包含一个小数点,否则会被当成整数类型处理。
- 科学计数形式:例如5.12e2 (即5.12x102)、5.12E2 (也是5.12x102)等。必须指出的是,只有浮点型的数才可以使用科学计数形式表示。例如51200是一个Int型的值,但512E2则是浮点型值。
如果声明一个常量或变量时没有指定数据类型,只是简单地指定了其初始值为浮点数,那么Kotlin会自动判断该变量的类型为Double。
除此之外,Kotin 还提供了3个特殊的浮点型数值:正无穷大、负无穷大和非数。例如,使用一个正数除以0.0将得到正无穷大数值,使用一个负数除以0.0将得到负无穷大数值,0.0除以0.0或对负数开方将得到一个非数。需要指出的是,所有的正无穷大数值都相等,所有的负无穷大数值都相等;而非数不与任何数值相等,甚至和非数自己都不相等。
fun main(args: Array<String>) {
var af1 = 5.2345556f
println("afl的值为:${af1}")
//声明af2是Float类型,但25.2345默认是Double类型,因此下面代码编译时报错
//var af2 : Float = 25.2345
// f1的类型被推断为Double
var f1 = 5.12e2
println("f1的值为:${f1}")
var a = 0.0
//5.0除以0.0将出现正无穷大数值
println("5.0/a的值为: ${5.0 / a}")
//所有的正无穷大数值都相等,所以下面将会输出true
println(5.0 / a == 50000 / 0.0)
// -5.0除以0.0将出现负无穷大数值
println("-5.0/a的值为: ${-5.0 / a}")
//所有的负无穷大数值都相等,所以下面将会输出true
println(-5.0 / a == -50000 / 0.0)
// 0.0除以0.0将出现非数
var nan: Double = a / a
println("a/a的值为: ${nan}")
//非数与自己都不相等,所以下面将会输出false
println(nan == nan)
}
字符型
字符型通常用于表示单个的字符,字符型值必须使用单引号(')括起来。Kotin语言使用16位的Unicode字符集作为编码方式,而Unicode被设计成支持世界上所有书面语言的字符,包括中文字符,因此Kotlin程序支持各种语言的字符。字符型值有如下3种表示形式。
- 直接通过单个字符来指定字符型值,例如'A'、 '9‘和0'等 。
- 通过转义字符表示特殊字符型值,例如"\n'、 "\t'等 。
- 直接使用Unicode值来表示字符型值,格式是"\uXXXX', 其中XXXX代表一个十六进制的整数。
Kotlin语言中常用的转义字符如表所示:
转义字符 | 说明 | Unicode表示方式 |
---|---|---|
\b | 退格符 | \u0008 |
\n | 换行符 | \u000a |
\r | 回车符 | \u000d |
\t | 制表符 | \u0009 |
" | 双引号 | \u0022 |
' | 单引号 | \u0027 |
\\ | 反斜线 | \u005c |
字符型值也可以采用十六进制编码方式来表示,范围是'\u0000' ~ '\uFFF',一共可以表示65536个字符,其中前256个(^u0000~\u00FF')字符和ASCII码中的字符完全重合。与Java不同的是,Kotin的Char型变量不能当成整数值使用,Char型变量或表达式不能赋值给整型变量,整型变量或表达式也不能赋值给Char型变量。简单来说,Kotlin 的Char型就是简简单单的字符型,不能直接当成整型使用。
字符型变量的用法:
fun main(args: Array<String>) {
//直接指定单个字符作为字符值
val aChar: Char = 'a'
//使用转义字符来作为字符值
val enterChar: Char ='\r'
//使用Unicode编码值来指定字符值
val ch: Char = '\u9999'
//将输出一个'香'字符
println(ch)
//定义一个'疯'字符值
var charValue: Char = '疯'
//将Char型变量当成Int型处理会报错
//var zhongValue: Int = charValue
}
数值型之间的类型转换
整型之间的转换
不同整型的变量能支持的表数范围是不同的,比如Byte类型的变量或常量只能接受-128 ~ 127之间的整数,Short类型的变量或常量只能接受-32768~32767之间的整数。如果数值超出了变量或常量所支持的表数范围,编译器就会报错。如下代码:
// Short类型支持的表数范围为-32768~32767,所以下面代码报错
//var negative: Short = 40000
// Byte类型支持的表数范围为-128~127,所以下面代码报错
//var big: Byte = 128
简单来说,Kotin与Java不同,Kotin不支持取值范围小的数据类型隐式转换为取值范围大的类型。由于不同整型支持的表数范围存在差异,因此进行类型转换时必须注意选择合适的类型。Kotlin为所有数值类型都提供了如下方法进行转换。
- toByte():转换为Byte类型。
- toShort():转换为Short类型
- tolnt():转换为Int类型。
- toLong():转换为Long类型。
- toFloat():转换为Float类型。
- toDouble():转换为Double类型。
- toChar(): 转换为Char类型。
Kotlin要求不同整型的变量或值之间必须进行显式转换。如下代码:
fun main(args: Array<String>) {
var bookPrice: Byte = 79
var itemPrice: Short = 120
// bookPrice是Byte类型,但变量a是Short类型,因此下面代码错误
// var a: Short = bookPrice
//显式将bookPrice强制转换为Short类型
var a: Short = bookPrice.toShort()
var b: Byte = itemPrice.toByte()
println("a: ${a}, b: ${b}")
val amount = 233
//将Int型变量转换为Byte类型,发生溢出
val byteAmount : Byte = amount.toByte ()
//输出-23
println (byteAmount)
}
虽然 Kotlin 缺乏隐式转换,但 Kotlin 在表达式中可以自动转换,这种转换是基于上下文推断出来的,而且算术运算会有重载做适当转换。如下代码:
fun main(args: Array<String>) {
var bookPrice: Byte = 79
var itemPrice: Short = 120
//算术表达式中的 bookPrice、 itemPrice 会自动提升为 Int 类型
var total = bookPrice + itemPrice
println("total 的值为 :${total}")
//可以看到 total 映射的 Java 类型为Int
println("total 的类型为:${total.javaClass}")
//下面表达式中的 bookPrice 强制转换为 Long 类型,因此整个表达式类型为 Long
val tot = bookPrice.toLong() + itemPrice.toByte()
println("total 的值为:${tot}")
//可以看到 total 映射的 Java 类型为 long
println("total 的类型为:${tot.javaClass}")
}
注意:上面程序中使用了变量的 javaClass属性,该属性来自 Any类型( Any类型是Kotlin.所有类型的根父类), javaClass 属性用于获取指定变量对应的 Java 类型(大致相当于 Java反射中的 getClass()方法)。
Kotiin 虽然不允许直接将 Char型值当成整数值使用,也不允许将整数值直接当成 Char型值使用,但 Kotlin依然可调用数值型的 toChar()方法将数值型变量或表达式转换为 Char类型。例如,下面程序示范了如何生成一个 6位的随机字符串。
fun main(args: Array<String>) {
//定义一个空字符串
var result = ""
//进行 6 次循环
for (i in 0..5) {
//生成一个 97~122 之间的 Int 类型整数
val intVal = (Math.random() * 26 + 97).toInt()
//将 intValue 强制转换为 Char 类型后连接到 result 后面
result += intVal.toChar()
}
//输出随机字符串,6个随机小写字母
println(result)
}
此外, Char 型值虽然不能被当成整数进行算术运算,但 Kotlin 为 Char 类型提供了加、减运算支持,其计算规则如下:
- Char 型值加、减一个整型值: Kotlin 会先将该 Char 型值对应的字符编码进行加、减该整数,然后将计算结果转换为 Char型值。
- 两个 Char 型值进行相减: Kotlin 将用两个 Char 型值对应的字符编码进行减法运算,最后返回 Int类型的值。两个 Char型值不能相加。
fun main(args: Array<String>) {
var cl = 'i'
var c2 = 'k'
println(cl + 4) // 输出 m
println(cl - 4) //输出 e
println((cl - c2)) //输出- 2
}
浮点型与整型之间的转换
Kotlin 的 Float、Double 之间需要进行显式转换,浮点型与整型之间也需要进行显式转换,其转换过程与前面介绍的整型之间的转换过程基本相似。例如:
fun main(args: Array<String>) {
var width: Float= 2.3f
var height: Double= 4.5
//width 必须显式强制转换为 Double 之后,才能赋值给变量a
var a : Double = width.toDouble()
println ("a的值为:${ a}")
//将 height 强制转换为 Float 之后再进行计算,整个表达式的类型是 Float
//因此 areal 的类型也被推断为 Float
var areal =width * height.toFloat()
//表达式中的 height 是 Double 类型,它是等级最高的运算数
// 因此整个表达式的类型是 Double, area2 的类型也被推断为 Double
var area2 = width * height
val multi: Int = 5
//因此 totalHeight1 的类型也被推断为 Double
var totalHeight1 = height * multi
//将 height 强制转换为 Int 类型后进行计算,整个表达式的类型是 Int
// 因此 totalHeight2 的类型也被推断为 Int
var totalHeight2 = height.toInt() * multi
}
通过上面的介绍不难发现,当进行类型转换时,应该尽量向表数范围大的数据类型转换, 这样程序会更加安全,比如前面介绍的 Byte 向 Short 转换、Int 向 Double 转换,而反过来转换则可能导致溢出。 Kotiin 语言的各种数值型的表数范围由小到大的顺序为 : Byte→ Short→ Int → Long→ Float→ Double。
表达式类型的自动提升
当一个算术表达式中包含多个数值型的值时,整个算术表达式的数据类型将发生自动提升。Kotlin定义了与Java基本相似的自动提升规则。
- 所有的 Byte、Short类型将被提升到 Int类型。
- 整个算数表达式的数据类型自动提升到与表达式最高等级操作数同样的类型。操作数的等级排列如下所示,位于箭头右边的类型等级高于位于箭头左边的类型等级。Byte→ Short→ Int → Long→ Float→ Double。
下面程序示范了一个典型的错误。
fun main(args: Array<String>) {
//定义一个 Short 类型变量
var sValue: Short = 5
//表达式中的 sValue 将自动提升到 Int 类型,则右边的表达式类型为Int
// 将一个 Int 类型值赋给 Short 类型变量将发生错误
//sValue = sValue - 2
}
下面是表达式类型自动提升的正确示例代码:
fun main(args: Array<String>) {
var b: Byte = 40
var c: Short = 97
var i: Int=23
var d: Double = .314
//右边表达式中最高等级的操作数为 d (Double 类型)
// 则右边表达式的类型为 Double, result 将会推断为 Double 类型
val result = b + c +i* d
//将输出 144.222
println(result)
}
必须指出,表达式的类型将严格保持和表达式中最高等级操作数相同的类型 。两个Int类型整数进行除法运算,即使无法除尽,也将得到一个Int类型结果。
如果表达式中包含了字符串 ,则又是另一番情形了。因为当把加号(+)放在字符串和数值之间时,这个加号是一个字符串连接运算符,而不是进行加法运算。