首页 / Go 语言入门教程 / 自定义类型与别名

Go 语言入门教程

自定义类型与别名

本教程共 80 篇 · 第 15 篇 · 更新于 2026-07-27 · 约 7 分钟阅读

GoGo 入门教程type自定义类型类型别名命名类型Go 1.24

15. 自定义类型与别名

本节目标:学会用 type 声明自定义类型,分清命名类型和类型别名的区别,了解 Go 1.24 泛型类型别名。

type 关键字

Go 用 type 关键字来定义新的类型。有三种用法:

  1. 定义命名类型
  2. 定义类型别名
  3. 定义结构体、接口(后面章节讲)

命名类型

type 新类型名 底层类型 来定义:

type Age int
type Score float64
type Name string

这会创建一个全新的类型。虽然 Age 的底层是 int,但 Ageint 是不同的类型:

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)  // 正确,通过函数转换

如果没有自定义类型,CelsiusFahrenheit 都是 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 的别名。它们不是新类型,是完全等价的。

别名的用途

别名主要用于:

  1. 简化长类型名:比如 type HandlerFunc = func(http.ResponseWriter, *http.Request)
  2. 渐进式重构:重命名类型时先建个别名保持兼容
  3. 代码迁移:包搬位置后用别名指向旧路径

命名类型 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 泛型类型别名的用法

下一节讲运算符。