《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函数帮助处理可空类型数据。

猜你喜欢

转载自blog.csdn.net/ming_147/article/details/79701628