Go 类型转换
类型转换允许你把一种数据类型的值当成另一种数据类型来处理。
在 Go 语言中,类型转换是绝对显式 (Explicit) 的。这意味着你必须清清楚楚地告诉编译器:“我要把这个值从 A 类型转换成 B 类型”。这与 JavaScript 或 C 等语言中常见的“隐式类型转换(编译器偷偷帮你转)”形成了鲜明对比。
1. 理解类型转换
在 Go 中,类型转换本质上是基于一个已有的值,去创建一个不同类型的新值。它的通用语法非常简单:
Type(value)其中,Type 是你想要转换成的目标类型,而 value 是你要转换的那个值或表达式。
注意: Go 并非允许你随意转换任何类型。为了保证数据的完整性并防止无意义的操作,Go 制定了严格的转换规则。
1.1 允许的转换 (Allowed Conversions)
只要两种类型的底层数据结构兼容,且转换意义明确,Go 通常会放行:
- 数字类型之间: 不同的整数之间(如
int转int64)、不同的浮点数之间(如float32转float64),以及整数与浮点数之间的互转,都是被允许的。但也暗藏杀机:把大整数转成小整数会导致数据截断溢出;把浮点数转成整数会直接丢失所有小数部分。 - 字符串与 Byte/Rune 切片: 你可以极其顺滑地将一个字符串 (string) 转换为一个字节切片
[]byte或一个字符切片[]rune,反之亦然。这在处理底层数据或中文字符时非常有用。 - 字符串与数字(必须借助
strconv包): 你不能用Type(value)的语法直接把int转成string(比如string(123)不会得到"123",而是一个奇怪的字符)。你必须使用 Go 内置的strconv包里的函数,比如strconv.Atoi(字符串转整数)或strconv.Itoa(整数转字符串)。
1.2 禁止的转换 (Disallowed Conversions)
Go 会严厉禁止那些可能导致行为未定义或数据损坏的转换:
- 布尔值与数字/字符串: 你绝对不能直接把
true/false转换成数字1/0或字符串"true"/"false",反过来也不行。 - 结构体 (Struct) 之间: 即便两个结构体包含一模一样的字段,只要它们的名字(类型)不同,就不能直接互转。
- 不兼容的切片: 你不能把一个
[]int直接转换为[]string。
2. 数字类型之间的转换
处理数字类型的互相转换是家常便饭,让我们仔细看看里面的门道。
2.1 整数转整数
在整数互转时,你最需要关心的是目标类型的容量够不够大。
package main
import "fmt"
func main() {
var i int32 = 1000
var j int64 = int64(i) // int32 转 int64,从小转大,绝对安全
fmt.Println(j)
var k int8 = int8(i) // int32 转 int8,从大转小,面临数据丢失风险
fmt.Println(k) // 输出: -24 (因为 1000 超出了 int8 的最大值 127,发生了内存溢出环绕)
}2.2 浮点数转整数
把浮点数转换为整数时,Go 采取的最简单粗暴的策略:直接砍掉所有小数部分(向下取整/截断)。
package main
import "fmt"
func main() {
var f float64 = 3.14
var i int = int(f) // float64 转 int
fmt.Println(i) // 输出: 3 (小数部分被无情抛弃)
}2.3 整数转浮点数
整数转浮点数通常很安全,但如果整数大得离谱,可能会丢失一定的精确度。
package main
import "fmt"
func main() {
var i int = 10000000000000001 // 一个极大的整数
var f float64 = float64(i) // int 转 float64
fmt.Println(f) // 输出: 1e+16 (由于 float64 的精度限制,最后一位的 '1' 可能丢失)
}3. 字符串 (String) 相关的转换
处理字符串的转换有点特殊。我们来看看如何进行底层的字节/字符转换,以及如何借助强大的 strconv 包处理数字互转。
3.1 字符串转 Byte/Rune 切片
[]byte将字符串拆解为原始的字节序列(常用于网络传输或文件写入)。[]rune将字符串拆解为 Unicode 码点序列(常用于正确处理中文字符)。
package main
import "fmt"
func main() {
str := "Hello, 世界"
// 字符串转字节切片
byteSlice := []byte(str)
fmt.Printf("%v\n", byteSlice) // 输出: [72 101 108 108 111 44 32 228 184 150 231 149 140] (注意中文字符占了多个字节)
// 字符串转字符切片 (Rune)
runeSlice := []rune(str)
fmt.Printf("%v\n", runeSlice) // 输出: [72 101 108 108 111 44 32 19990 30028] (中文字符被正确识别为独立码点)
}3.2 Byte/Rune 切片转回字符串
package main
import "fmt"
func main() {
byteSlice := []byte{72, 101, 108, 108, 111}
str1 := string(byteSlice)
fmt.Println(str1) // 输出: Hello
runeSlice := []rune{72, 101, 108, 108, 111, 32, 19990, 30028}
str2 := string(runeSlice)
fmt.Println(str2) // 输出: Hello 世界
}3.3 字符串转数字 (通过 strconv)
当你要把 "123" 变成真的数字 123 时,必须使用 strconv 包:
strconv.Atoi(ASCII to Integer):字符串转int。strconv.ParseFloat:字符串转float。
极度重要: 这类转换极有可能失败(比如你让它把 "abc" 转成数字),所以必须处理函数返回的错误 (err)。
package main
import (
"fmt"
"strconv"
)
func main() {
strInt := "123"
intVal, err := strconv.Atoi(strInt)
if err != nil {
fmt.Println("字符串转整数失败:", err)
return
}
fmt.Println(intVal) // 输出: 123
strFloat := "3.14"
floatVal, err := strconv.ParseFloat(strFloat, 64) // 64 代表 float64
if err != nil {
fmt.Println("字符串转浮点数失败:", err)
return
}
fmt.Println(floatVal) // 输出: 3.14
}3.4 数字转字符串 (通过 strconv)
strconv.Itoa(Integer to ASCII):int转字符串。strconv.FormatFloat:浮点数转字符串(可高度定制格式)。
package main
import (
"fmt"
"strconv"
)
func main() {
intVal := 42
strInt := strconv.Itoa(intVal)
fmt.Println(strInt) // 输出: "42"
floatVal := 2.71828
// 参数说明:'f' 表示常规小数格式,5 表示保留5位小数,64 表示源数据是 float64
strFloat := strconv.FormatFloat(floatVal, 'f', 5, 64)
fmt.Println(strFloat) // 输出: "2.71828"
}4. 实战演示
来看看类型转换在真实场景中的应用。
4.1 计算整数分数的平均值
如果你有一堆整数分数,想求出精确的平均分(带小数),你必须在做除法前把它们转换为浮点数。
package main
import "fmt"
func main() {
scores := []int{85, 92, 78, 95, 88}
sum := 0
for _, score := range scores {
sum += score
}
// 关键:如果不加 float64() 强转,整数除以整数还是整数,小数部分会被直接砍掉
average := float64(sum) / float64(len(scores))
fmt.Printf("平均分: %.2f\n", average) // 输出: 平均分: 87.60
}4.2 处理用户终端输入
从命令行读进来的所有东西都是字符串。你想做算术运算?先转换!
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("请输入一个数字: ")
// 读取用户输入,直到回车换行
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input) // 移除前后的空格和换行符
// 字符串转整数
num, err := strconv.Atoi(input)
if err != nil {
fmt.Println("输入无效,这不是一个数字:", err)
return
}
fmt.Printf("你输入的数字是: %d\n", num)
}4.3 将金额格式化输出
处理价格显示时,我们经常需要把计算好的浮点数,格式化为带两位小数的字符串用于界面显示。
package main
import (
"fmt"
"strconv"
)
func main() {
price := 49.99
formattedPrice := strconv.FormatFloat(price, 'f', 2, 64)
fmt.Printf("商品价格: $%s\n", formattedPrice) // 输出: 商品价格: $49.99
}