类型转换与推断
本教程共 80 篇 · 第 14 篇 · 更新于 2026-07-27 · 约 7 分钟阅读
14. 类型转换与推断
本节目标:掌握 Go 的显式类型转换语法,理解类型推断的工作方式,了解 Go 1.26 新增的 new(expr) 用法。
Go 没有隐式转换
很多语言会自动做类型转换。比如 C 语言里 int 加 float 会自动把 int 转成 float。Go 不行,Go 要求所有类型转换都必须显式写出来。
var a int = 10
var b float64 = 3.14
// c := a + b // 报错:mismatched types int and float64
c := float64(a) + b // 正确,先把 a 转成 float64
这个设计的好处是:你永远不会因为隐式转换而遇到意料之外的行为。代码里每一处类型转换都明明白白。
NoteGo 里
int和int32是不同的类型,即使int在 32 位平台上就是 32 位,也不能直接赋值,必须显式转换。
类型转换语法
格式:类型名(值)
数值类型之间
var i int = 42
var f float64 = float64(i) // int -> float64
var j int = int(f) // float64 -> int(截断小数部分)
fmt.Println(f) // 42
fmt.Println(j) // 42
var f2 float64 = 3.99
var i2 int = int(f2)
fmt.Println(i2) // 3,小数部分直接丢弃,不是四舍五入
整数和字符串之间
// 整数转字符串(按 Unicode 码点)
s := string(65)
fmt.Println(s) // A
字符串转整数需要用 strconv 包,记得在文件顶部导入:
package main
import (
"fmt"
"strconv"
)
func main() {
// 字符串转整数
n, err := strconv.Atoi("42")
fmt.Println(n, err) // 42 <nil>
// 整数转字符串(数字本身)
numStr := strconv.Itoa(42)
fmt.Println(numStr) // 42
}
Warning
string(65)得到的是"A"(码点 65 对应的字符),不是"65"。要把数字 42 转成字符串"42",用strconv.Itoa(42)。
字符串和字节切片
s := "Hello"
b := []byte(s) // string -> []byte
s2 := string(b) // []byte -> string
r := []rune(s) // string -> []rune
s3 := string(r) // []rune -> string
不同整数类型之间
var i32 int32 = 100
var i64 int64 = int64(i32) // int32 -> int64
var i int = int(i32) // int32 -> int
var u uint = uint(i64) // int64 -> uint
Tip大类型转小类型时要注意截断。比如
int64转int8,如果值超过 127,结果不可预期。
类型推断
Go 有类型推断,你在用 := 或 var x = value 时不需要写类型,编译器会自动推断。
基本推断
i := 42 // int
f := 3.14 // float64
s := "hello" // string
b := true // bool
r := 'A' // rune (int32)
从函数返回值推断
func getName() string {
return "Go"
}
name := getName() // 推断为 string
多变量推断
x, y := 10, 20 // 都是 int
a, b := 1, 3.14 // a 是 int,b 是 float64
字面量的类型推断
整数字面量默认是 int,如果赋值给特定类型的变量,会自动适配:
var i8 int8 = 42 // 42 适配为 int8
var f32 float32 = 3.14 // 3.14 适配为 float32
但如果字面量超出目标类型范围,编译器会报错:
var i8 int8 = 200 // 报错:constant 200 overflows int8
new 函数
new 是 Go 的内置函数,用来分配内存并返回指向零值的指针。
基本用法
p := new(int) // 分配一个 int,返回 *int
fmt.Println(*p) // 0(零值)
*p = 42
fmt.Println(*p) // 42
new(T) 返回 *T,指向一个类型为 T 的零值变量。
new 和直接声明的区别
// 方式一:直接声明
var x int = 42
// 方式二:new
p := new(int)
*p = 42
大部分时候你不需要 new,直接用 var 或 := 就行。new 主要用在需要指针的场景,比如结构体指针。
Go 1.26 新增:new(expr)
Go 1.26 给 new 增加了一个新能力:可以直接传入一个表达式作为初始值。
// Go 1.26 之前
age := 18
p := &age // 先声明变量再取地址
// Go 1.26 新写法
p := new(18) // 直接创建一个值为 18 的 *int
这在处理可选字段时特别有用。比如 JSON 里的可选字段用指针表示:
type Person struct {
Name string `json:"name"`
Age *int `json:"age"` // nil 表示不输出
}
// Go 1.26 之前,要设置 age 字段得先声明变量
yearsOld := 25
p := Person{Name: "Go", Age: &yearsOld}
// Go 1.26,一行搞定
p := Person{Name: "Go", Age: new(25)}
也可以传表达式:
func yearsSince(t time.Time) int {
return int(time.Since(t).Hours() / (365.25 * 24))
}
p := Person{
Name: "Go",
Age: new(yearsSince(birthday)),
}
Note
new(expr)是 Go 1.26 引入的新特性。它让可选指针字段的赋值变得简洁了很多。如果你的go.mod里go版本低于 1.26,用不了这个语法。
赋值与类型安全
Go 的类型系统在赋值时会严格检查:
var a int = 10
var b int64
// b = a // 报错:cannot use a as int64
b = int64(a) // 正确
即使是 int 和 int64,在 64 位平台上大小完全一样,也不能直接赋值。这是 Go 的设计选择:宁可多写类型转换,也不要隐式转换带来的模糊。
类型别名和转换
byte 是 uint8 的别名,rune 是 int32 的别名。别名类型之间不需要转换:
var b byte = 65
var u uint8 = b // 直接赋值,不需要转换,因为 byte 就是 uint8
var r rune = 65
var i int32 = r // 直接赋值,因为 rune 就是 int32
但 int 和 int32 不是别名关系(即使在 32 位平台上大小一样),需要显式转换。
常见的转换函数
除了类型转换语法,strconv 包提供了更多转换工具:
import "strconv"
// 字符串转整数
n, err := strconv.Atoi("42")
// 整数转字符串
s := strconv.Itoa(42)
// 字符串转 float
f, err := strconv.ParseFloat("3.14", 64)
// float 转字符串
fs := strconv.FormatFloat(3.14, 'f', 2, 64) // "3.14"
// 字符串转 bool
b, err := strconv.ParseBool("true")
学完这节你能做什么
- 理解 Go 为什么没有隐式转换
- 用
类型名(值)语法做显式转换 - 知道
string(65)和strconv.Itoa(65)的区别 - 用类型推断简化变量声明
- 用 Go 1.26 的
new(expr)简化可选指针赋值
下一节讲自定义类型和类型别名。