Go 语言类型转换
类型转换是编程中的重要概念,Go 语言提供了多种类型转换方式。本章将详细介绍显式类型转换、类型断言、字符串转换等内容。
📋 类型转换基础
显式类型转换
Go 语言不支持隐式类型转换,所有类型转换都必须显式进行。
go
package main
import "fmt"
func main() {
// 数值类型转换
var i int = 42
var f float64 = float64(i) // int 转 float64
var u uint = uint(i) // int 转 uint
fmt.Printf("int: %d, float64: %.2f, uint: %d\n", i, f, u)
// 精度可能丢失的转换
var bigFloat float64 = 123.456
var smallInt int = int(bigFloat) // 小数部分被截断
fmt.Printf("float64: %.3f -> int: %d\n", bigFloat, smallInt)
// 不同大小整数之间的转换
var big int64 = 1000000
var small int32 = int32(big)
fmt.Printf("int64: %d -> int32: %d\n", big, small)
// 字符和数字的转换
var char rune = 'A'
var ascii int = int(char)
var backToChar rune = rune(ascii)
fmt.Printf("字符: %c, ASCII: %d, 转回字符: %c\n", char, ascii, backToChar)
}类型转换规则
go
package main
import "fmt"
func main() {
fmt.Println("=== 数值类型转换规则 ===")
// 整数类型之间的转换
var a int8 = 127
var b int16 = int16(a)
var c int32 = int32(b)
var d int64 = int64(c)
fmt.Printf("int8(%d) -> int16(%d) -> int32(%d) -> int64(%d)\n", a, b, c, d)
// 浮点数精度转换
var f64 float64 = 3.141592653589793
var f32 float32 = float32(f64) // 精度降低
var backF64 float64 = float64(f32)
fmt.Printf("原始 float64: %.15f\n", f64)
fmt.Printf("转为 float32: %.15f\n", f32)
fmt.Printf("转回 float64: %.15f\n", backF64)
// 有符号和无符号转换
var signed int = -42
var unsigned uint = uint(signed) // 可能产生意外结果
fmt.Printf("有符号: %d -> 无符号: %d\n", signed, unsigned)
// 布尔值转换(需要手动处理)
var flag bool = true
var intFlag int
if flag {
intFlag = 1
} else {
intFlag = 0
}
fmt.Printf("布尔值: %v -> 整数: %d\n", flag, intFlag)
}🔤 字符串类型转换
strconv 包的使用
go
package main
import (
"fmt"
"strconv"
)
func main() {
fmt.Println("=== 字符串与数字转换 ===")
// 字符串转整数
str1 := "123"
if num1, err := strconv.Atoi(str1); err == nil {
fmt.Printf("字符串 '%s' -> 整数: %d\n", str1, num1)
} else {
fmt.Printf("转换失败: %v\n", err)
}
// 整数转字符串
num2 := 456
str2 := strconv.Itoa(num2)
fmt.Printf("整数 %d -> 字符串: '%s'\n", num2, str2)
// 字符串转浮点数
str3 := "3.14159"
if float1, err := strconv.ParseFloat(str3, 64); err == nil {
fmt.Printf("字符串 '%s' -> 浮点数: %.5f\n", str3, float1)
}
// 浮点数转字符串
float2 := 2.71828
str4 := strconv.FormatFloat(float2, 'f', 5, 64)
fmt.Printf("浮点数 %.5f -> 字符串: '%s'\n", float2, str4)
// 字符串转布尔值
str5 := "true"
if bool1, err := strconv.ParseBool(str5); err == nil {
fmt.Printf("字符串 '%s' -> 布尔值: %v\n", str5, bool1)
}
// 布尔值转字符串
bool2 := false
str6 := strconv.FormatBool(bool2)
fmt.Printf("布尔值 %v -> 字符串: '%s'\n", bool2, str6)
}进制转换
go
package main
import (
"fmt"
"strconv"
)
func main() {
fmt.Println("=== 进制转换 ===")
num := 255
// 十进制转其他进制
binary := strconv.FormatInt(int64(num), 2) // 二进制
octal := strconv.FormatInt(int64(num), 8) // 八进制
hex := strconv.FormatInt(int64(num), 16) // 十六进制
fmt.Printf("十进制 %d:\n", num)
fmt.Printf(" 二进制: %s\n", binary)
fmt.Printf(" 八进制: %s\n", octal)
fmt.Printf(" 十六进制: %s\n", hex)
// 其他进制转十进制
fmt.Println("\n转换回十进制:")
if val1, err := strconv.ParseInt(binary, 2, 64); err == nil {
fmt.Printf("二进制 '%s' -> 十进制: %d\n", binary, val1)
}
if val2, err := strconv.ParseInt(octal, 8, 64); err == nil {
fmt.Printf("八进制 '%s' -> 十进制: %d\n", octal, val2)
}
if val3, err := strconv.ParseInt(hex, 16, 64); err == nil {
fmt.Printf("十六进制 '%s' -> 十进制: %d\n", hex, val3)
}
// 任意进制转换
fmt.Println("\n任意进制转换:")
base36 := strconv.FormatInt(int64(num), 36) // 36进制
fmt.Printf("十进制 %d -> 36进制: %s\n", num, base36)
if val4, err := strconv.ParseInt(base36, 36, 64); err == nil {
fmt.Printf("36进制 '%s' -> 十进制: %d\n", base36, val4)
}
}🎯 接口类型转换
类型断言
go
package main
import "fmt"
func main() {
fmt.Println("=== 类型断言 ===")
// 创建接口变量
var i interface{} = "Hello, World!"
// 基本类型断言
if str, ok := i.(string); ok {
fmt.Printf("断言成功: %s (长度: %d)\n", str, len(str))
} else {
fmt.Println("断言失败: 不是字符串类型")
}
// 类型断言失败的情况
if num, ok := i.(int); ok {
fmt.Printf("是整数: %d\n", num)
} else {
fmt.Println("不是整数类型")
}
// 不安全的类型断言(可能panic)
defer func() {
if r := recover(); r != nil {
fmt.Printf("捕获panic: %v\n", r)
}
}()
// 这会触发panic,因为i不是int类型
// num := i.(int) // 取消注释会panic
fmt.Println("=== 多种类型处理 ===")
// 处理多种可能的类型
values := []interface{}{
42,
"Hello",
3.14,
true,
[]int{1, 2, 3},
}
for i, value := range values {
fmt.Printf("值 %d: ", i+1)
switch v := value.(type) {
case int:
fmt.Printf("整数: %d\n", v)
case string:
fmt.Printf("字符串: %s\n", v)
case float64:
fmt.Printf("浮点数: %.2f\n", v)
case bool:
fmt.Printf("布尔值: %v\n", v)
case []int:
fmt.Printf("整数切片: %v\n", v)
default:
fmt.Printf("未知类型: %T\n", v)
}
}
}接口转换示例
go
package main
import "fmt"
// 定义接口
type Shape interface {
Area() float64
}
type Drawable interface {
Draw()
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return 3.14159 * c.Radius * c.Radius
}
func (c Circle) Draw() {
fmt.Printf("绘制半径为 %.2f 的圆形\n", c.Radius)
}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Draw() {
fmt.Printf("绘制 %.2f x %.2f 的矩形\n", r.Width, r.Height)
}
func main() {
fmt.Println("=== 接口类型转换 ===")
shapes := []Shape{
Circle{Radius: 5},
Rectangle{Width: 4, Height: 3},
}
for i, shape := range shapes {
fmt.Printf("形状 %d:\n", i+1)
fmt.Printf(" 面积: %.2f\n", shape.Area())
// 类型断言检查是否实现了Drawable接口
if drawable, ok := shape.(Drawable); ok {
fmt.Print(" ")
drawable.Draw()
} else {
fmt.Println(" 不支持绘制")
}
// 使用类型断言获取具体类型
switch s := shape.(type) {
case Circle:
fmt.Printf(" 圆形半径: %.2f\n", s.Radius)
case Rectangle:
fmt.Printf(" 矩形尺寸: %.2f x %.2f\n", s.Width, s.Height)
}
fmt.Println()
}
}🔄 自定义类型转换
定义转换方法
go
package main
import (
"fmt"
"strconv"
"strings"
)
// 自定义类型
type Temperature float64
type Distance int
type Person struct {
Name string
Age int
}
// 温度转换方法
func (t Temperature) ToCelsius() Temperature {
return t
}
func (t Temperature) ToFahrenheit() Temperature {
return t*9/5 + 32
}
func (t Temperature) ToKelvin() Temperature {
return t + 273.15
}
func (t Temperature) String() string {
return fmt.Sprintf("%.2f°C", float64(t))
}
// 距离转换方法
func (d Distance) ToMeters() float64 {
return float64(d)
}
func (d Distance) ToKilometers() float64 {
return float64(d) / 1000
}
func (d Distance) ToMiles() float64 {
return float64(d) / 1609.34
}
func (d Distance) String() string {
return fmt.Sprintf("%d米", int(d))
}
// Person 转换方法
func (p Person) ToString() string {
return fmt.Sprintf("%s (%d岁)", p.Name, p.Age)
}
func (p Person) ToJSON() string {
return fmt.Sprintf(`{"name":"%s","age":%d}`, p.Name, p.Age)
}
func PersonFromString(s string) (Person, error) {
parts := strings.Split(s, ",")
if len(parts) != 2 {
return Person{}, fmt.Errorf("格式错误,应为: 姓名,年龄")
}
name := strings.TrimSpace(parts[0])
ageStr := strings.TrimSpace(parts[1])
age, err := strconv.Atoi(ageStr)
if err != nil {
return Person{}, fmt.Errorf("年龄格式错误: %v", err)
}
return Person{Name: name, Age: age}, nil
}
func main() {
fmt.Println("=== 自定义类型转换 ===")
// 温度转换
temp := Temperature(25)
fmt.Printf("温度转换:\n")
fmt.Printf(" 摄氏度: %s\n", temp.String())
fmt.Printf(" 华氏度: %.2f°F\n", temp.ToFahrenheit())
fmt.Printf(" 开尔文: %.2f K\n", temp.ToKelvin())
// 距离转换
distance := Distance(5000)
fmt.Printf("\n距离转换:\n")
fmt.Printf(" 米: %s\n", distance.String())
fmt.Printf(" 千米: %.2f公里\n", distance.ToKilometers())
fmt.Printf(" 英里: %.2f英里\n", distance.ToMiles())
// Person转换
person := Person{Name: "张三", Age: 25}
fmt.Printf("\nPerson转换:\n")
fmt.Printf(" 字符串: %s\n", person.ToString())
fmt.Printf(" JSON: %s\n", person.ToJSON())
// 从字符串创建Person
personStr := "李四, 30"
if p, err := PersonFromString(personStr); err == nil {
fmt.Printf(" 从字符串创建: %s\n", p.ToString())
} else {
fmt.Printf(" 创建失败: %v\n", err)
}
}⚠️ 类型转换的陷阱
精度损失和溢出
go
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println("=== 类型转换陷阱 ===")
// 1. 浮点数精度损失
fmt.Println("1. 浮点数精度损失:")
var precise float64 = 1.23456789012345
var imprecise float32 = float32(precise)
var backToPrecise float64 = float64(imprecise)
fmt.Printf("原始 float64: %.15f\n", precise)
fmt.Printf("转为 float32: %.15f\n", imprecise)
fmt.Printf("转回 float64: %.15f\n", backToPrecise)
fmt.Printf("精度损失: %.15f\n", precise-backToPrecise)
// 2. 整数溢出
fmt.Println("\n2. 整数溢出:")
var big int64 = math.MaxInt32 + 1
var small int32 = int32(big) // 溢出
fmt.Printf("原始 int64: %d\n", big)
fmt.Printf("转为 int32: %d\n", small)
fmt.Printf("最大 int32: %d\n", math.MaxInt32)
// 3. 有符号无符号转换陷阱
fmt.Println("\n3. 有符号无符号转换:")
var negative int = -100
var positive uint = uint(negative) // 危险!
fmt.Printf("有符号 int: %d\n", negative)
fmt.Printf("转为 uint: %d\n", positive)
// 4. 字符串转换失败
fmt.Println("\n4. 字符串转换错误处理:")
invalidStrings := []string{"abc", "12.34.56", "", "999999999999999999999"}
for _, s := range invalidStrings {
if num, err := strconv.Atoi(s); err != nil {
fmt.Printf("字符串 '%s' 转换失败: %v\n", s, err)
} else {
fmt.Printf("字符串 '%s' 转换成功: %d\n", s, num)
}
}
// 5. 类型断言失败
fmt.Println("\n5. 类型断言安全检查:")
var unknown interface{} = 3.14
// 安全的类型断言
if str, ok := unknown.(string); ok {
fmt.Printf("是字符串: %s\n", str)
} else {
fmt.Println("不是字符串类型")
}
// 获取实际类型
fmt.Printf("实际类型: %T, 值: %v\n", unknown, unknown)
}🛠️ 实用转换工具
通用转换工具函数
go
package main
import (
"fmt"
"reflect"
"strconv"
)
// 通用转换工具
type Converter struct{}
// 尝试将任意值转换为字符串
func (c Converter) ToString(value interface{}) string {
switch v := value.(type) {
case string:
return v
case int, int8, int16, int32, int64:
return fmt.Sprintf("%d", v)
case uint, uint8, uint16, uint32, uint64:
return fmt.Sprintf("%d", v)
case float32, float64:
return fmt.Sprintf("%g", v)
case bool:
return strconv.FormatBool(v)
case nil:
return ""
default:
return fmt.Sprintf("%v", v)
}
}
// 尝试将字符串转换为指定类型
func (c Converter) FromString(s string, targetType reflect.Type) (interface{}, error) {
switch targetType.Kind() {
case reflect.String:
return s, nil
case reflect.Int:
return strconv.Atoi(s)
case reflect.Int64:
return strconv.ParseInt(s, 10, 64)
case reflect.Float64:
return strconv.ParseFloat(s, 64)
case reflect.Bool:
return strconv.ParseBool(s)
default:
return nil, fmt.Errorf("不支持的类型: %v", targetType)
}
}
// 检查两个值是否可以进行类型转换
func (c Converter) CanConvert(from, to reflect.Type) bool {
return from.ConvertibleTo(to)
}
// 安全的类型转换
func (c Converter) SafeConvert(value interface{}, targetType reflect.Type) (interface{}, error) {
sourceType := reflect.TypeOf(value)
if sourceType == targetType {
return value, nil
}
if !sourceType.ConvertibleTo(targetType) {
return nil, fmt.Errorf("无法从 %v 转换到 %v", sourceType, targetType)
}
sourceValue := reflect.ValueOf(value)
convertedValue := sourceValue.Convert(targetType)
return convertedValue.Interface(), nil
}
func main() {
converter := Converter{}
fmt.Println("=== 通用转换工具测试 ===")
// 测试转换为字符串
values := []interface{}{
123,
3.14159,
true,
"hello",
nil,
[]int{1, 2, 3},
}
fmt.Println("转换为字符串:")
for _, value := range values {
str := converter.ToString(value)
fmt.Printf(" %T(%v) -> string(%s)\n", value, value, str)
}
// 测试从字符串转换
fmt.Println("\n从字符串转换:")
stringValues := map[string]reflect.Type{
"123": reflect.TypeOf(int(0)),
"3.14": reflect.TypeOf(float64(0)),
"true": reflect.TypeOf(bool(false)),
"hello": reflect.TypeOf(string("")),
}
for str, targetType := range stringValues {
if result, err := converter.FromString(str, targetType); err == nil {
fmt.Printf(" string(%s) -> %v(%v)\n", str, targetType, result)
} else {
fmt.Printf(" string(%s) -> %v 转换失败: %v\n", str, targetType, err)
}
}
// 测试安全转换
fmt.Println("\n安全类型转换:")
testCases := []struct {
value interface{}
target reflect.Type
}{
{int(42), reflect.TypeOf(float64(0))},
{float64(3.14), reflect.TypeOf(int(0))},
{true, reflect.TypeOf(int(0))},
{"hello", reflect.TypeOf(int(0))},
}
for _, tc := range testCases {
if result, err := converter.SafeConvert(tc.value, tc.target); err == nil {
fmt.Printf(" %T(%v) -> %v(%v)\n", tc.value, tc.value, tc.target, result)
} else {
fmt.Printf(" %T(%v) -> %v 转换失败: %v\n", tc.value, tc.value, tc.target, err)
}
}
}🎓 小结
本章我们全面学习了 Go 语言的类型转换:
- ✅ 显式转换:基本数据类型之间的转换
- ✅ 字符串转换:strconv 包的使用和进制转换
- ✅ 接口转换:类型断言和接口类型判断
- ✅ 自定义转换:为自定义类型添加转换方法
- ✅ 转换陷阱:精度损失、溢出等常见问题
- ✅ 实用工具:通用转换工具函数的实现
理解和掌握类型转换对于编写健壮的 Go 代码非常重要。
接下来,我们将学习 Go 语言接口,这是 Go 语言面向对象编程的核心特性。
类型转换建议
- 总是使用显式类型转换,Go 不支持隐式转换
- 注意数值转换可能的精度损失和溢出问题
- 使用类型断言时要检查返回的 ok 值
- 合理处理字符串转换可能出现的错误