1. 函数
Go 的函数是一等公民,支持多返回值、命名返回值、可变参数和闭包。
基本定义
// 基本函数
func add(a int, b int) int {
return a + b
}
// 相同类型参数可合并
func add2(a, b int) int {
return a + b
}
// 多返回值
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// 命名返回值(自动声明,return 时自动返回)
func rectInfo(width, height float64) (area, perimeter float64) {
area = width * height
perimeter = 2 * (width + height)
return // 等同于 return area, perimeter
}
可变参数
// 可变参数(类似 Java 的 varargs)
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
// 调用
sum(1, 2, 3) // 6
sum([]int{1, 2, 3}...) // 展开切片
函数作为值与闭包
package main
import "fmt"
func main() {
// 函数作为值
op := func(a, b int) int { return a + b }
fmt.Println(op(3, 4)) // 7
// 函数作为参数
result := apply(5, 3, func(a, b int) int {
return a * b
})
fmt.Println(result) // 15
// 闭包:捕获外部变量
counter := makeCounter()
fmt.Println(counter()) // 1
fmt.Println(counter()) // 2
fmt.Println(counter()) // 3
}
func apply(a, b int, fn func(int, int) int) int {
return fn(a, b)
}
func makeCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
Go 的特色:多返回值是语言核心特性,不是语法糖。惯用方式是将 error 作为最后一个返回值。命名返回值在短函数中提高可读性。
2. 结构体
Go 没有类(class),使用结构体(struct)+ 方法来组织数据和行为。
package main
import "fmt"
// 定义结构体
type User struct {
Name string
Email string
Age int
}
// 构造函数惯例:NewXxx
func NewUser(name, email string, age int) *User {
return &User{
Name: name,
Email: email,
Age: age,
}
}
// 结构体嵌入(组合 > 继承)
type Admin struct {
User // 匿名嵌入,自动提升字段和方法
Level int
}
// 带标签的结构体(用于 JSON、数据库映射)
type Article struct {
ID int `json:"id" db:"id"`
Title string `json:"title" db:"title"`
Content string `json:"content" db:"content"`
Published bool `json:"published" db:"published"`
}
func main() {
// 创建结构体
u1 := User{Name: "Alice", Email: "alice@example.com", Age: 30}
u2 := User{"Bob", "bob@example.com", 25} // 按顺序
u3 := NewUser("Charlie", "charlie@example.com", 28)
// 访问字段
fmt.Println(u1.Name, u1.Age)
// 修改字段(值类型,注意指针)
u3.Age = 29
// 嵌入结构体
admin := Admin{
User: User{Name: "Super", Email: "admin@example.com", Age: 35},
Level: 1,
}
fmt.Println(admin.Name) // 直接访问嵌入字段
fmt.Println(admin.Level)
fmt.Println(u1, u2, u3)
}
🔄 OOP 对比
| 概念 | Java/Python | Go |
| 类 | class | struct + 方法 |
| 继承 | extends | 嵌入(组合) |
| 接口 | implements(显式) | 隐式实现 |
| 构造器 | constructor/__init__ | NewXxx 工厂函数 |
3. 方法
方法是绑定到特定类型上的函数。接收者可以是值类型或指针类型。
package main
import (
"fmt"
"math"
)
type Circle struct {
Radius float64
}
// 值接收者:不修改原始值,适合只读操作
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
// 指针接收者:可以修改原始值,避免复制大结构体
func (c *Circle) Scale(factor float64) {
c.Radius *= factor
}
func (c Circle) String() string {
return fmt.Sprintf("Circle(r=%.2f)", c.Radius)
}
func main() {
c := Circle{Radius: 5}
fmt.Printf("面积: %.2f\n", c.Area()) // 78.54
fmt.Printf("周长: %.2f\n", c.Perimeter()) // 31.42
c.Scale(2) // 指针接收者,会修改 c
fmt.Printf("缩放后面积: %.2f\n", c.Area()) // 314.16
fmt.Println(c) // Circle(r=10.00)
}
值接收者 vs 指针接收者:
- • 值接收者
(c Circle):操作副本,不修改原始值,适合小型不可变结构体 - • 指针接收者
(c *Circle):操作原始值,可以修改字段,避免大结构体复制开销 - • 经验法则:如果任何方法用了指针接收者,那所有方法都应该用指针接收者
4. 接口
Go 的接口是隐式实现的——只要类型实现了接口的所有方法,就自动满足该接口。不需要 implements 关键字。
package main
import (
"fmt"
"math"
)
// 定义接口
type Shape interface {
Area() float64
Perimeter() float64
}
// Stringer 接口(类似 Java 的 toString)
type Stringer interface {
String() string
}
// Circle 隐式实现了 Shape
type Circle struct{ Radius float64 }
func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius }
func (c Circle) Perimeter() float64 { return 2 * math.Pi * c.Radius }
// Rectangle 也隐式实现了 Shape
type Rectangle struct{ Width, Height float64 }
func (r Rectangle) Area() float64 { return r.Width * r.Height }
func (r Rectangle) Perimeter() float64 { return 2 * (r.Width + r.Height) }
// 接口作为参数
func printShapeInfo(s Shape) {
fmt.Printf("面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())
}
// 空接口 any(Go 1.18+ 的别名)
func printAnything(v any) {
fmt.Printf("类型: %T, 值: %v\n", v, v)
}
func main() {
shapes := []Shape{
Circle{Radius: 5},
Rectangle{Width: 3, Height: 4},
}
for _, s := range shapes {
printShapeInfo(s)
}
// 类型断言
var s Shape = Circle{Radius: 3}
if c, ok := s.(Circle); ok {
fmt.Println("是圆形,半径:", c.Radius)
}
// 类型开关
checkType := func(v any) {
switch t := v.(type) {
case int:
fmt.Println("整数:", t)
case string:
fmt.Println("字符串:", t)
case Circle:
fmt.Println("圆形:", t.Radius)
default:
fmt.Printf("未知类型: %T\n", t)
}
}
checkType(42)
checkType("hello")
checkType(Circle{Radius: 7})
}
常用标准库接口
// io.Reader - 读取数据的通用抽象
type Reader interface {
Read(p []byte) (n int, err error)
}
// io.Writer - 写入数据的通用抽象
type Writer interface {
Write(p []byte) (n int, err error)
}
// fmt.Stringer - 自定义字符串表示
type Stringer interface {
String() string
}
// error - Go 的错误接口
type error interface {
Error() string
}
// sort.Interface - 可排序集合
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
隐式接口的优势:接口由使用方定义,而非实现方。这意味着你可以为第三方类型定义接口,无需修改其源码。这是 Go 接口设计的精髓。
5. 泛型(Go 1.18+)
Go 1.18 引入了泛型,通过类型参数实现类型安全的通用代码。
package main
import (
"fmt"
"cmp"
)
// 泛型函数:类型参数 [T]
func Max[T cmp.Ordered](a, b T) T {
if a > b {
return a
}
return b
}
// 自定义约束
type Number interface {
~int | ~int64 | ~float64
}
func Sum[T Number](nums []T) T {
var total T
for _, n := range nums {
total += n
}
return total
}
// 泛型结构体
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(item T) {
s.items = append(s.items, item)
}
func (s *Stack[T]) Pop() (T, bool) {
if len(s.items) == 0 {
var zero T
return zero, false
}
last := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return last, true
}
func (s *Stack[T]) Len() int {
return len(s.items)
}
// 泛型 Map 函数
func Map[T any, U any](slice []T, fn func(T) U) []U {
result := make([]U, len(slice))
for i, v := range slice {
result[i] = fn(v)
}
return result
}
func main() {
fmt.Println(Max(3, 7)) // 7
fmt.Println(Max("a", "z")) // z
fmt.Println(Sum([]int{1, 2, 3, 4, 5})) // 15
fmt.Println(Sum([]float64{1.1, 2.2, 3.3})) // 6.6
// 泛型栈
s := &Stack[string]{}
s.Push("hello")
s.Push("world")
val, _ := s.Pop()
fmt.Println(val) // world
// Map 转换
nums := []int{1, 2, 3, 4}
doubled := Map(nums, func(n int) int { return n * 2 })
fmt.Println(doubled) // [2 4 6 8]
strs := Map(nums, func(n int) string {
return fmt.Sprintf("#%d", n)
})
fmt.Println(strs) // [#1 #2 #3 #4]
}
🔄 泛型对比
| 语言 | 泛型语法 |
| Go | func Max[T cmp.Ordered](a, b T) T |
| Java | <T extends Comparable> T max(T a, T b) |
| TypeScript | function max<T>(a: T, b: T): T |
| Rust | fn max<T: Ord>(a: T, b: T) -> T |
Go 用方括号 [] 而非尖括号 <>,~int 表示底层类型是 int 的所有类型。
6. 本章要点
📌 函数
支持多返回值、命名返回值、可变参数。函数是一等公民,可作为值传递,闭包捕获外部变量。
📌 结构体
替代类,通过嵌入实现组合。NewXxx 工厂函数替代构造器,标签用于序列化映射。
📌 方法
值接收者操作副本,指针接收者操作原始值。如果需要修改结构体,必须用指针接收者。
📌 接口
隐式实现,无需 implements。小接口设计(如 io.Reader),any 是空接口别名。
📌 泛型
Go 1.18+ 支持类型参数,用 [] 声明。约束接口限定类型范围,~ 匹配底层类型。
📌 组合优于继承
Go 不支持类继承,通过结构体嵌入和接口组合实现代码复用,更灵活、更松耦合。