自定义类型与别名
本教程共 80 篇 · 第 15 篇 · 更新于 2026-07-27 · 约 7 分钟阅读
15. 自定义类型与别名
本节目标:学会用 type 声明自定义类型,分清命名类型和类型别名的区别,了解 Go 1.24 泛型类型别名。
type 关键字
Go 用 type 关键字来定义新的类型。有三种用法:
- 定义命名类型
- 定义类型别名
- 定义结构体、接口(后面章节讲)
命名类型
用 type 新类型名 底层类型 来定义:
type Age int
type Score float64
type Name string
这会创建一个全新的类型。虽然 Age 的底层是 int,但 Age 和 int 是不同的类型:
type Age int
var a Age = 18
var i int = 18
// i = a // 报错:cannot use a as int
i = int(a) // 正确,需要显式转换
// a = i // 报错
a = Age(i) // 正确
为什么要自定义类型
好处是类型安全和语义清晰:
type Celsius float64 // 摄氏度
type Fahrenheit float64 // 华氏度
func toFahrenheit(c Celsius) Fahrenheit {
return Fahrenheit(c*9/5 + 32)
}
var c Celsius = 100
var f Fahrenheit = 50
// f = c // 报错!不能把摄氏度直接赋给华氏度
f = toFahrenheit(c) // 正确,通过函数转换
如果没有自定义类型,Celsius 和 Fahrenheit 都是 float64,你就可能不小心把摄氏度当华氏度用,编译器也不会提醒你。
给类型加方法
自定义类型可以有自己的方法(方法后面章节详细讲):
type Celsius float64
func (c Celsius) String() string {
return fmt.Sprintf("%.1f°C", c)
}
func main() {
temp := Celsius(36.5)
fmt.Println(temp) // 36.5°C
}
实现了 String() 方法后,fmt.Println 打印这个类型时会自动调用它。
类型别名
用 type 新名 = 旧类型 (注意等号)来定义别名:
type MyInt = int
别名和原名是完全相同的类型,可以直接互相赋值:
type MyInt = int
var a MyInt = 10
var i int = 20
a = i // 正确,MyInt 就是 int
i = a // 也正确
Go 内置的两个别名你应该已经见过:
type byte = uint8
type rune = int32
byte 就是 uint8 的别名,rune 就是 int32 的别名。它们不是新类型,是完全等价的。
别名的用途
别名主要用于:
- 简化长类型名:比如
type HandlerFunc = func(http.ResponseWriter, *http.Request) - 渐进式重构:重命名类型时先建个别名保持兼容
- 代码迁移:包搬位置后用别名指向旧路径
命名类型 vs 别名
| 特性 | 命名类型 type T int | 别名 type T = int |
|---|---|---|
| 是否新类型 | 是 | 否 |
| 能否直接赋值给底层类型 | 不能,需要转换 | 能,是同一类型 |
| 能否加方法 | 能 | 不能 |
| 用途 | 类型安全、语义化 | 简化、兼容 |
Warning大部分时候你应该用命名类型(不带等号),因为类型安全更重要。只在需要兼容或简化长名字时才用别名。
自定义类型实战
枚举类型
type Status int
const (
Pending Status = iota
Active
Inactive
Deleted
)
func (s Status) String() string {
switch s {
case Pending:
return "待审核"
case Active:
return "活跃"
case Inactive:
return "未激活"
case Deleted:
return "已删除"
default:
return "未知"
}
}
func main() {
s := Active
fmt.Println(s) // 活跃
}
函数类型
type MathFunc func(int, int) int
func add(a, b int) int { return a + b }
func multiply(a, b int) int { return a * b }
func main() {
var f MathFunc = add
fmt.Println(f(3, 4)) // 7
f = multiply
fmt.Println(f(3, 4)) // 12
}
基于切片的自定义类型
type IntSlice []int
func (s IntSlice) Sum() int {
total := 0
for _, v := range s {
total += v
}
return total
}
func main() {
nums := IntSlice{1, 2, 3, 4, 5}
fmt.Println(nums.Sum()) // 15
}
泛型类型别名(Go 1.24)
Go 1.24 引入了泛型类型别名,允许你给泛型类型起别名。
先看一个普通的泛型类型:
type List[T any] struct {
items []T
}
Go 1.24 之后,你可以给它起个别名:
type IntList = List[int]
type StringList = List[string]
这样 IntList 就等于 List[int],StringList 等于 List[string],用起来更简洁。
func main() {
nums := IntList{items: []int{1, 2, 3}}
strs := StringList{items: []string{"a", "b"}}
fmt.Println(nums)
fmt.Println(strs)
}
Note泛型类型别名是 Go 1.24 引入的特性。在 1.24 之前,你不能给泛型类型创建别名。这个特性主要用于标准库和大型项目的类型简化。
类型定义的常见模式
基于字符串的 ID 类型
type UserID string
type ProductID string
func getUser(id UserID) User { ... }
func getProduct(id ProductID) Product { ... }
这样你就不可能把 UserID 传给 getProduct 函数,编译器会报错。
基于 time.Duration 的时间类型
type Timeout time.Duration
const (
ShortTimeout Timeout = Timeout(5 * time.Second)
LongTimeout Timeout = Timeout(30 * time.Second)
)
基于错误的自定义类型
type AppError struct {
Code int
Message string
}
func (e AppError) Error() string {
return fmt.Sprintf("[%d] %s", e.Code, e.Message)
}
实现了 Error() 方法,AppError 就是一个 error 接口的实现。
学完这节你能做什么
- 用
type声明命名类型,增加类型安全性 - 区分命名类型和类型别名的使用场景
- 给自定义类型添加方法
- 用自定义类型实现枚举
- 了解 Go 1.24 泛型类型别名的用法
下一节讲运算符。