要点
函数参数按值传递。指针参数传递的仍是指针的值,但能通过它访问原对象。方法只是把接收者加入调用约定,不自动获得引用语义。
函数基础
1. 函数定义语法
// 基本函数定义
func functionName(parameter1 type1, parameter2 type2) returnType {
// 函数体
return value
}
// 无参数函数
func sayHello() {
fmt.Println("Hello, World!")
}
// 带参数函数
func greet(name string) {
fmt.Printf("Hello, %s!n", name)
}
// 带返回值函数
func add(a, b int) int {
return a + b
}
2. 函数调用
package main
import "fmt"
// 定义一些示例函数
func add(a, b int) int {
return a + b
}
func multiply(x, y float64) float64 {
return x * y
}
func printMessage(msg string) {
fmt.Println(msg)
}
func main() {
// 调用无返回值函数
printMessage("程序开始")
// 调用有返回值函数
sum := add(10, 20)
fmt.Printf("10 + 20 = %dn", sum)
product := multiply(3.5, 2.0)
fmt.Printf("3.5 × 2.0 = %.2fn", product)
// 直接使用函数返回值
fmt.Printf("5 + 3 = %dn", add(5, 3))
printMessage("程序结束")
}
函数参数
1. 值参数
// 值传递 - 函数内修改不影响外部变量
func modifyValue(x int) {
x = 100 // 只影响函数内的副本
fmt.Printf("函数内: x = %dn", x)
}
func main() {
num := 10
fmt.Printf("调用前: num = %dn", num)
modifyValue(num)
fmt.Printf("调用后: num = %dn", num) // 仍然是10
}
2. 引用参数(指针)
// 指针传递 - 可以修改外部变量
func modifyByReference(ptr *int) {
*ptr = 100 // 修改指针指向的值
fmt.Printf("函数内: *ptr = %dn", *ptr)
}
func main() {
num := 10
fmt.Printf("调用前: num = %dn", num)
modifyByReference(&num) // 传递地址
fmt.Printf("调用后: num = %dn", num) // 变为100
}
3. 可变参数
// 可变参数函数
func sum(numbers ...int) int {
total := 0
for _, num := range numbers {
total += num
}
return total
}
// 混合参数
func greetAll(greeting string, names ...string) {
for _, name := range names {
fmt.Printf("%s, %s!n", greeting, name)
}
}
func main() {
// 可变参数调用
result1 := sum(1, 2, 3, 4, 5)
fmt.Printf("1+2+3+4+5 = %dn", result1)
result2 := sum(10, 20)
fmt.Printf("10+20 = %dn", result2)
// 混合参数调用
greetAll("你好", "张三", "李四", "王五")
}
返回值
1. 单个返回值
// 返回单个值
func getPI() float64 {
return 3.14159
}
func isEven(num int) bool {
return num%2 == 0
}
2. 多个返回值
// 返回多个值
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为0")
}
return a / b, nil
}
// 返回多个不同类型的值
func getUserInfo(id int) (string, int, bool) {
// 模拟数据库查询
if id == 1 {
return "张三", 25, true
} else if id == 2 {
return "李四", 30, true
}
return "", 0, false
}
func main() {
// 多返回值接收
name, age, found := getUserInfo(1)
if found {
fmt.Printf("用户: %s, 年龄: %dn", name, age)
}
// 错误处理
result, err := divide(10, 2)
if err != nil {
fmt.Printf("错误: %vn", err)
} else {
fmt.Printf("结果: %.2fn", result)
}
}
3. 命名返回值
// 命名返回值 - 可以直接使用return语句
func calculate(a, b int) (sum, diff int) {
sum = a + b
diff = a - b
return // 等价于 return sum, diff
}
// 命名返回值可以在函数内直接使用
func process(name string) (result string, err error) {
if name == "" {
err = errors.New("姓名不能为空")
return // 返回空字符串和错误
}
result = "处理成功: " + name
return // 返回结果和nil错误
}
func main() {
sum, diff := calculate(10, 3)
fmt.Printf("和: %d, 差: %dn", sum, diff)
result, err := process("张三")
if err != nil {
fmt.Printf("错误: %vn", err)
} else {
fmt.Printf("结果: %sn", result)
}
}
函数作为值
1. 函数类型
// 定义函数类型
type Calculator func(int, int) int
// 定义函数
func add(a, b int) int {
return a + b
}
func subtract(a, b int) int {
return a - b
}
// 接受函数作为参数
func calculate(a, b int, op Calculator) int {
return op(a, b)
}
func main() {
// 将函数作为参数传递
result1 := calculate(10, 5, add)
fmt.Printf("10 + 5 = %dn", result1)
result2 := calculate(10, 5, subtract)
fmt.Printf("10 - 5 = %dn", result2)
}
2. 匿名函数
func main() {
// 匿名函数赋值给变量
add := func(a, b int) int {
return a + b
}
result := add(3, 4)
fmt.Printf("3 + 4 = %dn", result)
// 在其他函数中使用匿名函数
numbers := []int{1, 2, 3, 4, 5}
filtered := filter(numbers, func(n int) bool {
return n%2 == 0 // 只保留偶数
})
fmt.Printf("偶数: %vn", filtered)
}
// 过滤函数
func filter(numbers []int, predicate func(int) bool) []int {
var result []int
for _, num := range numbers {
if predicate(num) {
result = append(result, num)
}
}
return result
}
闭包
// 返回函数的函数
func makeAdder(x int) func(int) int {
return func(y int) int {
return x + y // 闭包:访问外部变量x
}
}
// 计数器工厂
func makeCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
// 闭包示例1
add5 := makeAdder(5)
add10 := makeAdder(10)
fmt.Printf("5 + 3 = %dn", add5(3)) // 8
fmt.Printf("10 + 7 = %dn", add10(7)) // 17
// 闭包示例2 - 计数器
counter1 := makeCounter()
counter2 := makeCounter()
fmt.Printf("计数器1: %dn", counter1()) // 1
fmt.Printf("计数器1: %dn", counter1()) // 2
fmt.Printf("计数器2: %dn", counter2()) // 1 (独立状态)
fmt.Printf("计数器1: %dn", counter1()) // 3
}
方法
1. 方法定义
方法是与特定类型关联的函数:
// 定义结构体
type Rectangle struct {
Width float64
Height float64
}
// 定义方法 - 值接收者
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// 定义方法 - 指针接收者(可以修改结构体)
func (r *Rectangle) SetDimensions(width, height float64) {
r.Width = width
r.Height = height
}
func (r Rectangle) String() string {
return fmt.Sprintf("Rectangle{Width: %.1f, Height: %.1f}", r.Width, r.Height)
}
func main() {
// 创建结构体实例
rect := Rectangle{Width: 10.0, Height: 5.0}
// 调用方法
area := rect.Area()
fmt.Printf("面积: %.2fn", area)
// 调用指针接收者方法
rect.SetDimensions(20.0, 10.0)
area = rect.Area()
fmt.Printf("新面积: %.2fn", area)
// String()方法会被fmt.Printf自动调用
fmt.Printf("矩形: %sn", rect)
}
2. 值接收者 vs 指针接收者
type Counter struct {
count int
}
// 值接收者方法 - 不能修改原始值
func (c Counter) Increment() {
c.count++
fmt.Printf("值接收者内部: %dn", c.count)
}
// 指针接收者方法 - 可以修改原始值
func (c *Counter) IncrementPtr() {
c.count++
fmt.Printf("指针接收者内部: %dn", c.count)
}
func main() {
counter := Counter{count: 0}
fmt.Printf("初始值: %dn", counter.count)
// 调用值接收者方法
counter.Increment()
fmt.Printf("值接收者调用后: %dn", counter.count) // 仍然是0
// 调用指针接收者方法
counter.IncrementPtr()
fmt.Printf("指针接收者调用后: %dn", counter.count) // 变为1
}
下面这段是可独立运行的最小对照:值接收者修改的是副本;指针接收者通过地址修改原对象。
package main
import "fmt"
type Counter struct{ count int }
func (c Counter) Increment() { c.count++; fmt.Println("值接收者内部:", c.count) }
func (c *Counter) IncrementPtr() { c.count++; fmt.Println("指针接收者内部:", c.count) }
func main() {
c := Counter{}
fmt.Println("初始值:", c.count)
c.Increment()
fmt.Println("值接收者调用后:", c.count)
c.IncrementPtr()
fmt.Println("指针接收者调用后:", c.count)
}
接口初识
方法的价值在于支撑接口(interface)——Go 实现多态的核心机制。这里只建立直觉,完整语义见 Go OOP 概念字典。
// 接口:只声明"能做什么",不声明"是什么"
type Speaker interface {
Speak() string
}
// 任何类型只要实现了 Speak() string,就自动满足 Speaker —— 无需声明
type Dog struct{ Name string }
type Robot struct{ ID int }
func (d Dog) Speak() string { return d.Name + ": 汪汪" }
func (r Robot) Speak() string { return fmt.Sprintf("Robot-%d: 嘀嘀", r.ID) }
// 面向接口编程:函数不关心具体类型
func greet(s Speaker) {
fmt.Println(s.Speak())
}
func main() {
greet(Dog{Name: "旺财"}) // 旺财: 汪汪
greet(Robot{ID: 7}) // Robot-7: 嘀嘀
// 接口是"装具体值"的盒子
speakers := []Speaker{Dog{Name: "阿黄"}, Robot{ID: 42}}
for _, s := range speakers {
greet(s)
}
}
三个关键点: 1. 隐式实现:类型实现接口不需要 implements 声明,方法集匹配即可——这是 Go 与其他主流语言最不同的设计 2. 方法接收者决定实现:值接收者方法同时属于值和指针;指针接收者方法只属于指针类型 3. 接口值是盒子:Speaker 变量可以装入任何实现了它的值,运行时按装入的具体类型分派
方法接收者选值还是指针,往往取决于”这个类型要满足哪些接口、接口方法要不要改状态”——把本节与上面的值/指针接收者对比结合起来理解。
实际应用示例
示例1: 数学工具库
package main
import (
"fmt"
"math"
)
// 数学操作函数
func power(base, exponent float64) float64 {
return math.Pow(base, exponent)
}
func sqrt(number float64) (float64, error) {
if number < 0 {
return 0, fmt.Errorf("不能计算负数的平方根")
}
return math.Sqrt(number), nil
}
// 批量计算函数
func batchCalculate(numbers []float64, operation func(float64) float64) []float64 {
results := make([]float64, len(numbers))
for i, num := range numbers {
results[i] = operation(num)
}
return results
}
func main() {
// 基本数学运算
result := power(2, 3)
fmt.Printf("2³ = %.0fn", result)
// 错误处理
sqrtResult, err := sqrt(16)
if err != nil {
fmt.Printf("错误: %vn", err)
} else {
fmt.Printf("√16 = %.2fn", sqrtResult)
}
// 批量计算
numbers := []float64{1, 4, 9, 16, 25}
squareRoots := batchCalculate(numbers, math.Sqrt)
fmt.Printf("平方根: %vn", squareRoots)
}
示例2: 字符串处理工具
package main
import (
"fmt"
"strings"
"unicode"
)
// 字符串处理函数
func reverseString(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
func isPalindrome(s string) bool {
// 转换为小写并移除非字母字符
cleaned := strings.Map(func(r rune) rune {
if unicode.IsLetter(r) {
return unicode.ToLower(r)
}
return -1
}, s)
return cleaned == reverseString(cleaned)
}
// 高阶函数 - 字符串处理管道
func StringPipeline(s string, operations ...func(string) string) string {
result := s
for _, op := range operations {
result = op(result)
}
return result
}
func main() {
// 字符串反转
original := "Hello, World!"
reversed := reverseString(original)
fmt.Printf("原字符串: %sn", original)
fmt.Printf("反转字符串: %sn", reversed)
// 回文检测
testWords := []string{"level", "hello", "racecar", "world"}
for _, word := range testWords {
fmt.Printf("%s 是回文: %tn", word, isPalindrome(word))
}
// 字符串处理管道
text := " Hello, World! "
operations := []func(string) string{
strings.TrimSpace, // 移除前后空格
strings.ToLower, // 转小写
func(s string) string { return strings.ReplaceAll(s, ",", " ") }, // 替换逗号
}
processed := StringPipeline(text, operations...)
fmt.Printf("处理后: '%s'n", processed)
}
示例3: 验证工具
package main
import (
"fmt"
"regexp"
"unicode"
)
// 验证函数类型
type Validator func(string) bool
// 常用验证函数
func ValidateEmail(email string) bool {
pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$`
matched, _ := regexp.MatchString(pattern, email)
return matched
}
func ValidatePhone(phone string) bool {
pattern := `^1[3-9]d{9}$` // 简单的中国手机号验证
matched, _ := regexp.MatchString(pattern, phone)
return matched
}
func ValidatePassword(password string) bool {
if len(password) < 8 {
return false
}
var hasUpper, hasLower, hasDigit, hasSpecial bool
for _, char := range password {
switch {
case unicode.IsUpper(char):
hasUpper = true
case unicode.IsLower(char):
hasLower = true
case unicode.IsDigit(char):
hasDigit = true
case unicode.IsPunct(char) || unicode.IsSymbol(char):
hasSpecial = true
}
}
return hasUpper && hasLower && hasDigit && hasSpecial
}
// 组合验证器
func ValidateAll(input string, validators ...Validator) bool {
for _, validator := range validators {
if !validator(input) {
return false
}
}
return true
}
func main() {
// 单个验证
email := "user@example.com"
fmt.Printf("邮箱 %s 有效: %tn", email, ValidateEmail(email))
phone := "13812345678"
fmt.Printf("手机号 %s 有效: %tn", phone, ValidatePhone(phone))
password := "SecurePass123!"
fmt.Printf("密码强度满足要求: %tn", ValidatePassword(password))
// 组合验证 - 验证用户输入
userInput := struct {
email string
phone string
password string
}{
email: "test@example.com",
phone: "13987654321",
password: "MySecure123!",
}
isValid := ValidateAll(
userInput.email,
ValidateEmail,
ValidatePhone,
)
fmt.Printf("用户输入验证: %tn", isValid)
// 密码单独校验
fmt.Printf("密码强度满足要求: %tn", ValidatePassword(userInput.password))
}
函数设计最佳实践
1. 单一职责原则
// ✅ 好的设计 - 每个函数只做一件事
func calculateTax(income float64) float64 {
return income * 0.2 // 简单的税收计算
}
func formatCurrency(amount float64) string {
return fmt.Sprintf("¥%.2f", amount)
}
// ❌ 不好的设计 - 一个函数做太多事
func calculateAndFormatTax(income float64) string {
tax := income * 0.2
return fmt.Sprintf("¥%.2f", tax)
}
2. 纯函数
// ✅ 纯函数 - 相同输入总是产生相同输出
func add(a, b int) int {
return a + b
}
// ❌ 非纯函数 - 有副作用
func addAndPrint(a, b int) int {
result := a + b
fmt.Printf("结果: %dn", result) // 副作用
return result
}
3. 错误处理
// ✅ 好的错误处理
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("除数不能为0")
}
return a / b, nil
}
// 使用错误处理
result, err := divide(10, 2)
if err != nil {
log.Printf("除法错误: %v", err)
return
}
fmt.Printf("结果: %.2f", result)
文档交叉引用
相关文档
参考资源
- 📖 [Go 函数文档]: https://golang.org/ref/spec#Functions
- 📖 [Go 方法文档]: https://golang.org/ref/spec#Method_declarations
- 📖 [Go 闭包文档]: https://golang.org/ref/spec#Function_literals
总结
核心要点回顾
- 函数定义: 掌握函数语法和参数传递方式
- 返回值: 理解单返回值、多返回值和命名返回值
- 方法使用: 区分函数和方法,掌握接收者概念
- 高阶函数: 学会使用闭包和函数作为参数
实践练习
- [ ] 编写不同参数类型的函数
- [ ] 创建返回多个值的函数
- [ ] 实现带错误处理的函数
- [ ] 定义结构体并创建相关方法
文档状态: ✅ 已完成最后更新: 2026年9月版本: v1.0.0
💡 学习建议: – 多练习函数的定义和调用,熟悉 Go 的语法特性 – 理解值传递和引用传递的区别,合理选择使用场景 – 掌握 Go 的多返回值特性,用于替代异常处理 – 学会使用闭包创建工厂函数和状态管理