《Kotlin进化之路》之【第二章:揭开Kotlin的基础面纱】(二)
2.1 基本数据类型
2.1.1:数据类型概括
在 Kotlin 中,所有变量的成员方法和属性都是一个对象,一些类型是内建的,它们的实现是优化过的,但对用户来说它们就像普通的类一样,在这节中,将会讲到大多数的类型:数值,字符,布尔,以及数组。
Kotlin 的基本数值类型包括 Byte、Short、Int、Long、Float、Double 等,会Java的同学觉着这个和Java类似,但需要注意的是,字符不属于数值类型,是一个独立的数据类型,具体的数据类型及位宽度,详见图2.1.1.1。
(图2.1.1.1)
上图中忽略了取值范围,其实这个学过Java的同学应该有所了解,对于刚接触到高级语言的同学可能略显生疏,下面我就在列一张表,仔细的来对比一下:
类型 |
位深度 |
取值范围 |
Double |
64(8个字节) |
-263~263-1 |
Float |
32(4个字节) |
-231~231-1 |
Long |
64(8个字节) |
-263~263-1 |
Int |
32(4个字节) |
-231~231-1 |
Short |
16(2个字节) |
-215~215-1 |
Byte |
8(1个字节) |
-128~127 |
如何去表示一个字面常量,对于十进制的表示方法,我们都会,无非就是:123……,那么其它进制呢?
2进制以0b开头:0b00001011
16进制表示方法以0x开头:0x0F
长整形以大写的L结尾:66666666L
这里有一点需要注意,Kotlin是不支持8进制的。
2.1.2:如何去定义一个常量或者变量
定义Kotlin的常量或者变量,它的语法和JavaScript的语法有点类似,用var来定义可变变量,用val来定义不可变变量,基本语法如下:
Var <标识符> : <类型> = <初始化值>
Val <标识符> : <类型> = <初始化值>
在Kotlin中val代表只读,var代表可变,对于这两个,开发中如果你暂时不知道用哪个的时候,我的建议是尽可能多的使用val,因为val是线程安全的,并且必须在定义时初始化,所以不需要担心 null 的问题,只需要注意 val 在某些情况下也是可变的就行了。
对于区别,我的理解如下:
val 和 var 是用于表示属性是否有 getter/setter:
var:同时有 getter 和 setter
val:只有 getter
所以,能用val的地方就用val。
具体使用如下:
var a : Int = 66
val b : Int = 88
【须知】如果变量或常量的数据类型,有可能采用自动类型推导,那么我们就可以省去类型,直接去赋值也是可以的,这样显得代码更加的简洁:
val a = 66
val b = 88
注意:由于val是不可变的,如果我们再次赋值,那么就会在编译之前出一个错误:Val cannot be reassigned,提示我们这个不能再重新分配:如图2.1.2.1。
(图2.1.2.1)
对于上面的一小段代码,可能有的同学蒙了,咋没有分号做结尾呢,这里需要说明一点的是,kotlin取消了分割语句的分号,而选择在换行时检查当前parser状态以及下一行第一个token决定是否结束语句,这个和Python的语法有点类似。
对于标识符这里我简单啰嗦几句,在实际的开发中,我希望大家能词能达意,也就是说,自己取得名字得符合公司的规定,让人有一种,见了这个标识符,就知道这个变量或者常量是干嘛用的,当然了,这里的标识符我为了方便,就简单化了。
关于Kotlin标识符,有以下几点需要注意:
所有的标识符都应该以字母(A-Z或者a-z)、或者下划线(_)开始
首字符之后可以是字母(A-Z或者a-z),下划线(_)或数字的任何字符组合
关键字不能用作标识符
标识符是大小写敏感的
合法标识符举例:age、_value、__1_value
非法标识符举例:123abc、-salary
举例2.1.2.1,输出各个类型的数据:
fun main(args: Array<String>) {
val a:Byte = 127 //Byte类型
val b:Short = -127 //Short类型
val c:Long = 88888888L //Long类型
val d : Int = 88 //Int类型
val e :Float = 88.88F //Float类型
val f:Double = 88.888 //Double类型
val g = 0x1F //十六进制数
val h = 0b00001001 //二进制数
println(a)
println(b)
println(c)
println(d)
println(e)
println(f)
println(g)
println(h)
}
打印结果:
127
-127
88888888
88
88.88
88.888
31
9
对于数据特别大的情况下,比如1000000000这个数值,为了显示的有层次美观,我们可以进行下划线来表示:
val a = 1_000_000_000
println(a)
打印结果:
1000000000
同样,其它类型的数据也可以这样操作,举例2.1.2.2:
fun main(args: Array<String>) {
val a = 888_888_888_888L
val b = 666_666_666L
val c = 0xFF_EC_DE_5E
val d = 0b11010010_01101001_10010100_10010010
println(a)
println(b)
println(c)
println(d)
}
打印结果:
888888888888
666666666
4293713502
3530134674
2.1.3:静态类型(理解)
Kotlin 和 Java 一样是一种静态类型的编程语言,这意味着所有表达式的类型在编译期已经确定了,而编译器就能验证对象是否包含了你想访问的方法或者字段。
这与动态类型 的编程语言形成了鲜明的对 比,后者在 JVM 上的代表包括 Groovy 和 JRuby。这些语言允许你定义可以存储任何数据类型的变量,或者返回任 何数据类型的函数,并在运行时才解析方法和字段引用。这会减少代码量并增加创 建数据结构的灵活性。但它的缺点是,在编译期不能发现像名字拼写错误这样的问题,继而导致运行时的错误。
另一方面,与 Java 不同的是,Kotlin 不需要你在源代码中显式地声明每个变量 的类型。很多情况下,变量类型可以根据上下文来自动判断,这样就可以省略类型声明。这里有一个可能是最简单的例子 :
val x = 1
在声明这个变量时,由于变量初始化为整型值,Kotlin 自动判断出它的类型是 Int。编译器这种从上下文推断变量类型的能力被称作类型推导。
下面罗列了一些静态类型带来的好处 :
性能——方法调用速度更快,因为不需要在运行时才来判断调用的是哪个方法。
可靠性——编译器验证了程序的正确性,因而运行时崩溃的概率更低。
可维护性——陌生代码更容易维护,因为你可以看到代码中用到的对象的类型。
工具支持——静态类型使 IDE 能提供可靠的重构、精确的代码补全以及其他 特性。
得益于 Kotlin 对类型推导的支持,你不再需要显式地声明类型,因此大部分关 于静态类型的额外冗长代码也就不复存在了。
当你检视 Kotlin 类型系统的细节时,你会发现许多熟悉的概念,类、接口以及 泛型和 Java 非常接近,所以大部分的 Java 知识可以很容易地转移到 Kotlin,然而, 也会有一些新概念出现。
其中最重要的概念是 Kotlin 对可空类型的支持,通过在编译期检测可能存在的 空指针异常,它让你可以写出更可靠的程序。
另一个 Kotlin 类型系统的新概念是对函数类型的支持,要搞清楚这一点,我们先要了解函数式编程的主要思想,以及 Kotlin 是如何支持这种编程风格的。
2.1.4:类型转换
在开发中,我们经常会遇到数据类型之间的转换,在Kotlin中由于表示方法的不同,较小类型并不是较大类型的子类型,较小的类型不能隐式转换为较大的类型,意味着在不进行显示转换的情况下我们不可以把Byte类型赋值给一个Int类型,如图2.1.4.1:
(图2.1.4.1)
2.1.4.1:显示转换
较小的类型不会被隐式转换为更大的类型,故而系统提供了显式转换,提供的显式转换方法如下:
Ø toByte() => 转换为字节型
Ø toShort() => 转换为短整型
Ø toInt() => 转换为整型
Ø toLong() => 转换为长整型
Ø toFloat() => 转换为浮点型
Ø toDouble() => 转换为双精度浮点型
Ø toChar() => 转换为字符型
Ø toString() => 转换为字符串型
举例2.1.4.1:
fun main(args: Array<String>) {
var a: Int = 88
println(a.toByte())
println(a.toShort())
println(a.toInt())
println(a.toLong())
println(a.toFloat())
println(a.toDouble())
println(a.toChar())
println(a.toString())
}
打印结果:
88
88
88
88
88.0
88.0
X
88
2.1.4.2:隐式转换
类型是从上下文推断出来的,即算术运算则被重载为适当的转换
举例2.1.4.2:
fun main(args: Array<String>) {
val num = 88L + 88 //Long + Int => Long
print(num)
}
打印结果:
176
2.1.5:字符型(Char)
和 Java 不一样,Kotlin 中的 Char 不能直接和数字操作,Char 必需是单引号 ' 包含起来的,比如普通字符 '0','a'。
fun main(args: Array<String>) {
var char1: Char
char1 = 'a'
char1 = 1 //这句代码会直接出错
println("char1 => $char1")
}
字符型的变量不仅可以转换为数字,同时也可转换为其他类型,举例2.1.5.1:
fun main(args: Array<String>) {
var char1:Char='a'
var var1 = char1.toByte()
var var2 = char1.toInt()
var var3 = char1.toString()
var var4 = char1.toFloat()
var var5 = char1.toShort()
println("var1 => $var1 \n var2 => $var2 \n var3 => $var3 \n var4 => $var4 \n var5 => $var5")
}
打印结果:
var1 => 97
var2 => 97
var3 => a
var4 => 97.0
var5 => 97
除了可以转换类型外,当变量为英文字母时还支持大小写转换,举例2.1.5.2:
fun main(args: Array<String>) {
/*
当字符变量为英文字母时,大小写的转换
*/
var charA: Char = 'a'
var charB: Char = 'B'
var charNum: Char = '1'
var result: Char
// 转换为大写
result = charA.toUpperCase()
println("result => $result")
// 转换为小写
result = charB.toLowerCase()
println("result => $result")
//当字符变量不为英文字母时,转换无效
result = charNum.toLowerCase()
println("result => $result")
}
打印结果:
result => A
result => b
result => 1
字符转义同Java一样,使用某些特殊的字符时,要使用转义。下列是支持的转义序列:
Ø \t => 表示制表符
Ø \n => 表示换行符
Ø \b => 表示退格键(键盘上的Back建)
Ø \r => 表示键盘上的Enter键
Ø \\ => 表示反斜杠
Ø \' => 表示单引号
Ø \" => 表示双引号
Ø \$ => 表示美元符号,如果不转义在kotlin中就表示变量的引用了
Ø 其他的任何字符请使用Unicode转义序列语法。例:'\uFF00'
举例2.1.5.3:
fun main(args: Array<String>) {
println("\n 换行符")
println("\t 制表符")
println(" \b 退格键")
println("\r Enter键同样换行")
println('\\')
println('\'')
println('\"')
println('\$')
println('\uFF01')
}
打印结果:
换行符
制表符
退格键
Enter键同样换行
\
'
"
$
!
2.1.6:布尔类型(Boolean)
Boolean关键字表示布尔类型,并且其值有true和false
举例2.1.6.1:
fun main(args: Array<String>) {
var boolTrue:Boolean=true
var boolFalse:Boolean=false
println(boolTrue)
println(boolFalse)
}
打印结果:
true
false
2.1.7:可空类型
一门新的语言的诞生总有它特定的和别的语言不同的一点,Kotlin也不例外,Kotlin语言与Swift语言类似,默认情况下所有的数据类型都是非空类型(Non-Null),声明的变量都是不能接收空值(null)的,这一点与Java和Objective-C等语言有很大的不同。
Kotlin的非空类型设计可以防止空指针异常(NullPointerException),在Kotlin中如果将一个对象的声明为非空类型,那么它就永远不会接收空值,否则会发生编译错误。
代码演示:
fun main(args: Array<String>) {
var a:Int = 66
a = null
}
上述代码会抛出一个异常,因为Int是非空类型,它所声明的变量a不能接收空值,但有些场景确实没有数据,例如查询数据库记录时,没有查询出符合条件的数据是很正常的事情,为此,Kotlin为每一种非空类型提供对应的可空类型(Nullable),就是在非空类型后面加上问号(?)表示可空类型。
修改后代码则就没有了问题:
fun main(args: Array<String>) {
var a:Int? = 66
a = null
}
Int?是可空类型,它所声明的变量a可以接收空值,可空类型在具体使用时会有一些限制:
Ø 不能直接调用可空类型对象的函数或属性。
Ø 不能把可空类型数据赋值给非空类型变量。
Ø 不能把可空类型数据传递给非空类型参数的函数。
为了“突破”这些限制,Kotlin提供了如下运算符:
Ø 安全调用运算符:?.
Ø 安全转换运算符:as?
Ø Elvis运算符:?:
Ø 非空断言:!!
Ø 此外,还一个let函数帮助处理可空类型数据。