Kotlin是一門強類型的語言,因此編譯器對類型檢查非常嚴格,這種嚴格保證了Kotlin程序的健壯性,因此Kotlin不同類型的值經常需要進行相互轉換,數值型之間的變量和值也可以相互轉換。
一、整型之間的轉換
1.1 不同整型的變量能支持的表數範圍
不同整型的變量能支持的表數範圍是不同的。 如下所示:
整型類型 | 表數範圍 |
---|---|
Byte | Byte型證書在內存中通常佔8位,表數範圍是 -128~127。 兼容Java的byte和Byte類型 |
Short | Short型證書在內存中通常佔16位,表數範圍是 -32768(-2的15次方)~32767(2的15次方減1)。 兼容Java的short和Short類型 |
Int | Int型證書在內存中通常佔32位,表數範圍是 -2147483648(-2的31次方)~2147483647(2的31次方減1)。 兼容Java的int和Int類型 |
Long | Long型證書在內存中通常佔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!