Kotlin是一门强类型的语言,因此编译器对类型检查非常严格,这种严格保证了Kotlin程序的健壮性,因此Kotlin不同类型的值经常需要进行相互转换,数值型之间的变量和值也可以相互转换。

一、整型之间的转换

1.1 不同整型的变量能支持的表数范围

不同整型的变量能支持的表数范围是不同的。 如下所示:

整型类型表数范围
ByteByte型证书在内存中通常占8位,表数范围是 -128~127。 兼容Java的byte和Byte类型
ShortShort型证书在内存中通常占16位,表数范围是 -32768(-2的15次方)~32767(2的15次方减1)。 兼容Java的short和Short类型
IntInt型证书在内存中通常占32位,表数范围是 -2147483648(-2的31次方)~2147483647(2的31次方减1)。 兼容Java的int和Int类型
LongLong型证书在内存中通常占64位,表数范围是 --2的63次方~2的63次方减1。 兼容Java的long和Long类型

1.2 如果数值超出了变量或常量所支持的表数范围,编译器就会报错。

因此,如果数值超出了变量或常量所支持的表数范围,编译器就会报错。

比如下面的代码

  //Short类型支持的表数范围是 -32768 ~ 32767 ,所以下面代码报错
    var a : Short = 40000

    //Byte类型支持的表数范围是 -128 ~ 127 ,所以下面代码报错
    var b : Byte = 128

在这里插入图片描述

简单来说,Kotlin和Java不同,Kotlin不支持取值范围小的数据类型隐式转换成取值范围大的类型。

1.3 进行类型转换时必须选择合适的类型

由于不同整型支持的表数范围存在差异,因此进行类型转换时必须选择合适的类型。

Kotlin为所有数值类型都提供了如下方法进行转换。

方法描述
toByte()转换成Byte类型
toShort()转换成Short类型
toInt()转换成Int类型
toLong()转换成Long类型
toFloat()转换成Float类型
toDouble()转换成Double类型
toChar()转换成Char类型

Kotlin要求不同整型的变量或值直接必须进行显示转换。

比如下面的代码:

fun intConvert() {
    var byteValue: Byte = 79
    var shortValue: Short = 120

    //byteValue是Byte类型,但是变量a是Short类型,无法强转
    //错误: Type mismatch. Required: Short     Found: Byte
//    var a :Short = byteValue

    //显示将bookPrice强制转换成Short类型
    var a: Short = byteValue.toShort()
    var b: Byte = shortValue.toByte()

    println("a:${a},b:${b}")

    val intValue = 233
    //将Int类型变量转换成Byte类型,发生溢出  值变成了 -23
    val c: Byte = intValue.toByte()
    println("c ${c}")
}

在这里插入图片描述

与Java类似,把取值范围大的变量或表达式强制转换为取值范围小的类型时,可能发生溢出。

上面程序把233强制转换成Byte类型证书,从而变成了-23 ,这是典型的溢出。下图示范了这个转换过程。

在这里插入图片描述

32位的Int类型的233在内存中的存储示意如上图所示,强制转换成8位的Byte类型,需要截断前面的24位,只保留右边的8位,其中最左边的1是符号位,因此表明这是一个负数。

负数在计算机中是以补码形式存在的,因此还需要转换成原码。

将补码加1得到反码形式,再将反码取反就可以得到原码。

最后得到的二进制原码是 10010111,这个Byte类型的值为 -(16+4+2+1),也就是 -23.

从什么的例子,我们很容易看出,当试图把表数范围大的类型强制转换为表数范围小的类型时,必须格外小心,因为很容易引起信息丢失。

1.4 Kotlin在表达式中自动转换

虽然Kotlin缺乏隐式转换,但是Kotlin在表达式中可以自动转换,这种转换是基于上下文推断出来的,而且算数运算会有重载做适当转换。

如下代码所示:


fun autoConvert(){
    var byteValue: Byte = 79
    var shortValue: Short = 120

    // 算数表达式中的byteValue和shortValue 会自动提升为Int类型
    var total = byteValue + shortValue
    println("total = ${total}")
    // 可以看到total映射的Java类型是int
    println("total的类型为 = ${total.javaClass}")

    // byteValue强制转换为Long类型,这样整个表达式最高等级的操作数类型是Long类型
    // 因此整个表达式计算得到的结果也是Long类型
    val tot = byteValue.toLong() + shortValue.toByte()
    println("tot = ${tot}")
    // 可以看到tot映射的Java类型是long
    println("tot的类型为 = ${tot.javaClass}")
}

在这里插入图片描述

1.5 Char型 加、减运算

此外,Char型值虽然不能被当成整数进行算数运算,但是Kotlin为Char类型提供了加、减运算支持。其计算规则如下所示:

Char型值 加、减一个整数值
Kotlin会先将Char型值对应的字符编码进行加、减该整数,然后将计算结果转换成Char型值。

两个Char型值进行加、减
最后返回Int类型的值。两个Char型值不能相加。

示例程序:

fun charAddAndSubtract(){
    // A B C D E F G H I J K

    var c1 = 'A'
    var c2 = 'K'

    // 输出 E
    println(c1 + 4)
    // 输出 G
    println(c2 - 4)
    // 输出 -10
    println((c1-c2))
}

在这里插入图片描述

二、浮点型与整型之间的转换

Kotlin的Float、Double直接需要进行显示转换,浮点型与整型之间也需要进行显示转换。

示例程序

fun floatConvert() {
    var width: Float = 2.3f
    var height: Double = 4.5
    // width必须显示强制换行为Double之后,才能赋值给变量a
    // 错误Type mismatch.Required:Double   Found:Float
//    var a : Double = width
    var a: Double = width.toDouble()
    println("a的值为: ${a}")

    // 将height强制转换为Float之后在进行计算,整个表达式类型为Float
    // 因此area1的类型也被推断为Float
    var area1 = width * height.toFloat()
    println("area1的值为: ${area1}")
    println("area1的类型为: ${area1.javaClass}")

    // 表达式中height是Double类型,它是等级最高的运算数
    // 因此整个表达式类型为Double
    // 因此area2的类型也被推断为Double
    var area2 = width * height
    println("area2的值为: ${area2}")
    println("area2的类型为: ${area2.javaClass}")

    var multi: Int = 5
    // height是Double类型,因此totalHeight1的类型被推断为Double
    var totalHeight1 = height * multi
    println("totalHeight1的值为: ${totalHeight1}")
    println("totalHeight1的类型为: ${totalHeight1.javaClass}")

    //将height强制转换为Int之后在进行计算,整个表达式类型为Int
    // 因此area1的类型也被推断为Int
    var totalHeight2 = height.toInt() * multi
    println("totalHeight2的值为: ${totalHeight2}")
    println("totalHeight2的类型为: ${totalHeight2.javaClass}")
}

在这里插入图片描述
通过上面示例,我们可以发现,

当进行类型转换的时候,应该尽量向表数范围大的数据类型转换,这样程序才会更加安全。

Kotlin语言的各种数值型表数范围由小到大的顺序是 Byte–>Short–>Int–>Long–>Float–>Double

三、表达式类型的自动提示

当一个算数表达式中包含多个数值型的值时,整个算是表达式的数据类型将自动提升。
Kotlin定义了和Java基本相似的自动提升规则。

  • 所有的Byte、Short类型会被提升到Int 类型
  • 整个算数表达式的数据类型自动提升到与表达式中最高等级操作数相同的类型。

操作数的等级排列如下所示,位于箭头右边的类型等级高于位于箭头左边的类型等级。
在这里插入图片描述

  • 错误示范
fun autoPromote(){
    // 定义一个Short类型的变量
    var shortValue:Short = 5

    // 表达式中的shortValue被自动提升为Int类型,则右边的表达式类型为Int
    // 将一个Int类型的值赋值给Short类型变量会发送错误
    // 报错  Type mismatch.Required:  Short Found:Int
    shortValue = shortValue -2
}

在这里插入图片描述

  • 正确示范
fun autoPromote() {
    var b: Byte = 40
    var s: Short = 97
    var i: Int = 23
    var d: Double = .314

    // 表达式中d是Double类型,它是等级最高的运算数
    // 因此整个表达式类型为Double
    // 因此result的类型也被推断为Double
    val result = b + s + i * d
    // 输出  result = 144.222
    println("result = ${result}")
}

在这里插入图片描述

可以看到结论:表达式的类型将严格和表达式中最高等级操作数相同的类型。

  • 两个Int相除示范
fun autoPromote() {
    var intValue = 3
    // 右边表达式中的两个操作数都是Int类型,所以右边表达式的类型是Int
    // 虽然 23/3 不能除尽,但是依然得到一个Int类型整数
    val intResult = 23 / intValue
    // 输出 intResult = 7
    println("intResult = ${intResult}")
}

结论: 两个整数进行除法运算时,如果不能整除,得到的结果是将小数部分截断取整后的整数。

  • 表达式包含字符串

如果表达式中包含了字符串,则又要另外细说了,因为:

当把加号(+)放在字符串和数值直接时,这个加哈是一个字符串连接运算符,而不是进行加法运算。

  //    输出字符串   Hello!a7
    println("Hello!" + 'a' + 7)
    //    输出字符串    hHello!
    println( 'a' + 7 + "Hello!")

第一个表达式 “Hello!” + ‘a’ + 7 ,先进行 “Hello!” + ‘a’ 运算,把‘a’转换成字符串,拼接成字符串“Hello!a” 然后进行“Hello!a” + 7的运算,这是一个字符串连接运算,得到的结果是Hello!a7

第二个表达式 ‘a’ + 7 + “Hello!”,先进行 ‘a’ + 7加法运算,此时是对Char型值做加法运算,将会得到’a’后面的第7个字符,因此得到‘h’; 接着进行’h’ + “Hello!”运算,‘h’自动转换成字符串,变成了两个字符串的连接运算,因此得到 hHello!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐