第二十一回:Go 语言类型转换

祝枝山也能变美女?
唐伯虎
唐伯虎: 祝兄,虽然你长得... 潦草,但只要化化妆,戴个头套,也能变成"美女"。
在 Go 语言里,把一种类型的数据变成另一种类型,就叫类型转换

🎭 强制易容术 (数值转换)

Go 语言非常严格,不同类型(哪怕都是整数)不能直接混合运算,必须手动转换。

语法: Type(Value)

秋香
秋香: 华安,我要分苹果。3个苹果分给2个人,结果是 1 还是 1.5 呀?
唐伯虎
唐伯虎: 这得看你怎么切!如果是整数除法 3/2,那就只能得 1(多余的扔了)。想得 1.5,就得先把苹果变成“浮点苹果”!
package main
import "fmt"

func main() {
    // 整数除法陷阱
    fmt.Println(3 / 2) // 输出 1 (而不是 1.5)

    // 正确做法:先转换类型
    a := 3
    b := 2
    // 必须把两边都转成 float64 才能进行小数运算
    fmt.Println(float64(a) / float64(b)) // 输出 1.5
    
    // 场景:秋香买菜
    price := 3.99 // float64
    count := 10   // int
    
    // total := price * count // ❌ 报错!float64 和 int 不能直接乘
    
    // ✅ 正确:把 int 转成 float64
    total := price * float64(count) 
    fmt.Println("总价:", total) // 39.9
    
    // ⚠️ 警告:把 float 转 int 会直接截断(不是四舍五入)
    money := int(total) 
    fmt.Println("老板只收整数:", money) // 39 (亏了0.9)
    
    // 💡 小贴士:如果想四舍五入,可以 +0.5 再截断
    rounded := int(total + 0.5)
    fmt.Println("四舍五入:", rounded) // 40
}
华夫人
华夫人: 哼!把大的转成小的(比如 int64 转 int8),可能会丢数据(溢出),就像祝枝山硬穿秋香的衣服,会撑破的!

🔢 数字与字符串的跨界 (strconv 包)

要把字符串 "9527" 变成数字 9527,不能直接用 int("9527"),得请出 strconv 工具箱。

1. 字符串转数字 (Atoi / ParseInt)

import "strconv"

str := "9527"
// Atoi (Ascii to Integer) 是最常用的
num, err := strconv.Atoi(str)
if err != nil {
    fmt.Println("这根本不是数字!")
} else {
    fmt.Println("工号是:", num) // 9527 (int类型)
}

// 高级用法:ParseInt (可以指定进制和位数)
// "FF" 转成 16进制的整数
i64, _ := strconv.ParseInt("FF", 16, 64)
fmt.Println(i64) // 255

2. 数字转字符串 (Itoa / FormatInt)

age := 18
// Itoa (Integer to Ascii)
str := strconv.Itoa(age)
fmt.Println("秋香今年 " + str + " 岁") // 字符串拼接

// 浮点数转字符串 (保留2位小数)
pi := 3.1415926
s := fmt.Sprintf("%.2f", pi) // 最简单的方法
fmt.Println(s) // "3.14"

🎭 易容术的最高境界 (类型定义 vs 类型别名)

在 Go 语言里,我们可以给类型起新名字,但这其中有玄机。

1. 类型定义 (Type Definition) - 创建新身份

type NewType OldType

这就像唐伯虎变成了“9527”。虽然骨子里都是男人 (int),但在 Go 看来,9527 和 int 是两种不同的生物,不能直接混用!

type ServantID int // 定义一个新类型 ServantID

func main() {
    var id ServantID = 9527
    var num int = 10
    
    // fmt.Println(id + num) // ❌ 报错!类型不同不能相加
    
    // 必须强制转换:把 9527 打回原形 (int)
    fmt.Println(int(id) + num) // ✅ 输出 9537
}

2. 类型别名 (Type Alias) - 只是换个马甲

type NewType = OldType (注意有个等号)

这就像“华安”只是“9527”的别名。他们完全是一回事,可以直接混用。

type HuaAn = int // 华安就是 int 的别名

func main() {
    var h HuaAn = 9527
    var n int = 10
    
    // 可以直接相加,不需要转换
    fmt.Println(h + n) // ✅ 输出 9537
}

🔤 汉字的秘密:Byte vs Rune

唐伯虎写了两个字 "秋香"。这两个字在计算机里怎么存?

秋香
秋香: 华安,你数数我的名字有几个字?数错了就不理你了!
name := "秋香"

// ❌ 错误做法:直接转 byte 切片
bytes := []byte(name)
fmt.Println("字节数:", len(bytes)) // 输出 6 (因为每个汉字UTF-8编码占3字节)

// ✅ 正确做法:转 rune (字符) 切片
runes := []rune(name)
fmt.Println("字符数:", len(runes)) // 输出 2 (准确识别汉字)

// 修改其中一个字
// bytes[0] = '春' // ❌ 这样做会破坏 UTF-8 编码,变成乱码
runes[0] = '春'
fmt.Println(string(runes)) // 输出:春香

// 💡 最佳实践:遍历字符串
// range 循环会自动把字符串按照 rune (字符) 遍历,不会乱码
for i, char := range name {
    fmt.Printf("第 %d 个字是:%c\n", i, char)
}

🎯 练功房(反转诗句)

把 "我爱秋香" 这句话倒过来念(变成 "香秋爱我")。

package main
import "fmt"

func main() {
    s := "我爱秋香"
    // 填空:先转成 rune 切片
    runes := []______(s)
    
    // 简单的双指针反转
    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
        runes[i], runes[j] = runes[j], runes[i]
    }
    
    // 填空:最后转回 string
    fmt.Println(______(runes))
}

任务: 填补类型转换代码。

答案:

runes := []rune(s)   // 转成 rune 切片才能处理汉字
fmt.Println(string(runes)) // 转回字符串