面向对象:结构体与接口
本章讲解Go面向对象:struct定义与初始化、方法与接收者、接口隐式实现、组合优于继承。重点:Go没有类继承,用struct+interface实现组合;理解值接收者vs指针接收者的区别。
前置知识:第4章 类型系统 学习目标:掌握Go OOP核心概念、理解接口值和nil行为
6.1 结构体:Go的”类”
Section titled “6.1 结构体:Go的”类””// Python classclass User: def __init__(self, name string, age int): self.name = name self.age = age// Go structtype User struct { Name string Age int}
// 创建实例user := User{Name: "Alice", Age: 30}
// 指定字段创建user := User{ Name: "Alice", Age: 30,}
// 字段顺序(不推荐)user := User{"Alice", 30}type User struct { Name string Age int Email string}
// 全部字段u1 := User{Name: "Alice", Age: 30, Email: "alice@example.com"}
// 部分字段(其他用零值)u2 := User{Name: "Bob"}// u2.Age = 0, u2.Email = ""
// 字段名加冒号u3 := User{Name: "Carol", Age: 25}
// 顺序初始化u4 := User{"Dave", 35} // 不推荐,可读性差user := User{Name: "Alice", Age: 30}
fmt.Println(user.Name) // Alicefmt.Println(user.Age) // 30
user.Age = 31 // 修改字段fmt.Println(user.Age) // 31指针访问字段
Section titled “指针访问字段”user := User{Name: "Alice", Age: 30}
// 取地址p := &user
// 指针访问字段(语法糖,自动解引用)fmt.Println(p.Name) // Alice,等同于(*p).Namep.Age = 31 // 等同于(*p).Age = 316.2 方法与接收者
Section titled “6.2 方法与接收者”本节目的:理解Go方法语法、值接收者vs指针接收者的区别
Go的方法与函数不同,方法有接收者:
type User struct { Name string Age int}
// 定义方法(value receiver)func (u User) Greet() { fmt.Printf("Hello, I'm %s, %d years old\n", u.Name, u.Age)}
// 调用user := User{Name: "Alice", Age: 30}user.Greet() // Hello, I'm Alice, 30 years oldValue Receiver(值接收者):
func (u User) SetName(name string) { u.Name = name // 修改的是副本}
user := User{Name: "Alice", Age: 30}user.SetName("Bob")fmt.Println(user.Name) // Alice(未修改)Pointer Receiver(指针接收者):
func (u *User) SetName(name string) { u.Name = name // 修改原对象}
user := User{Name: "Alice", Age: 30}user.SetName("Bob")fmt.Println(user.Name) // Bob(已修改)何时用指针接收者
Section titled “何时用指针接收者”通常遵循这个原则:
// 1. 需要修改对象时func (u *User) SetAge(age int) { u.Age = age}
// 2. 对象较大时(避免拷贝)func (u *User) HeavyOperation() { /* ... */ }
// 3. 统一风格(通常全用指针)
// 注:Go会自动转换,user.SetAge(25)等效于(&user).SetAge(25)方法与Python类对比
Section titled “方法与Python类对比”class User: def __init__(self, name: str, age: int): self.name = name self.age = age
def greet(self): print(f"Hello, I'm {self.name}, {self.age} years old")
def set_age(self, age: int): self.age = age
user = User("Alice", 30)user.greet()user.set_age(31)主要差异:
- Python:方法第一个参数是self
- Go:方法有接收者,接收者可以是值或指针
方法集(Method Sets)
Section titled “方法集(Method Sets)”值接收者的方法,无法修改对象:
type User struct { Name string Age int}
func (u User) SetName(name string) { u.Name = name}指针接收者的方法,可以修改对象:
func (u *User) SetAge(age int) { u.Age = age}结构体嵌入(类似继承)
Section titled “结构体嵌入(类似继承)”type Address struct { City string Country string}
type User struct { Name string Address // 嵌入(匿名字段)}
// 使用user := User{ Name: "Alice", Address: Address{ City: "Beijing", Country: "China", },}
fmt.Println(user.City) // Beijing(直接访问嵌入字段)fmt.Println(user.Address.City) // BeijingPython继承:
class Address: def __init__(self, city, country): self.city = city self.country = country
class User: def __init__(self, name, address): self.name = name self.address = address6.3 接口:隐式实现
Section titled “6.3 接口:隐式实现”本节目的:理解Go接口隐式实现、掌握接口值和nil的处理
type Writer interface { Write([]byte) (int, error)}任何实现了Write([]byte) (int, error)的类型,都实现了Writer接口。无需显式声明”实现Writer”。
type User struct { Name string}
// User实现了Writer接口// 因为User有Write([]byte) (int, error)方法func (u *User) Write(data []byte) (int, error) { fmt.Printf("Writing: %s\n", string(data)) return len(data), nil}
// 检查是否实现接口var _ Writer = (*User)(nil) // 编译时检查// Error接口type error interface { Error() string}
// Stringer接口type Stringer interface { String() string}
// Reader接口type Reader interface { Read(p []byte) (n int, err error)}
// Writer接口type Writer interface { Write(p []byte) (n int, err error)}
// Closer接口type Closer interface { Close() error}// 空接口表示"任何类型"var i interface{} = 10var s interface{} = "hello"var user interface{} = User{Name: "Alice", Age: 30}
// 函数接受任意类型func printValue(v interface{}) { fmt.Println(v)}
// 切片可以存储任意类型var items []interface{} = []interface{}{1, "hello", User{}}接口值由两部分组成:动态类型和动态值:
var writer Writer
// nil接口writer = nil // 动态类型=nil,动态值=nil
// 带值的接口writer = &User{Name: "Alice"}// 动态类型=*User,动态值=&User{Name: "Alice"}
// 调用方法writer.Write([]byte("test")) // 调用*User的Write方法var writer Writer
// writer == nil 为 true// writer.Write(nil) 会panic(调用nil上的方法)var i interface{} = "hello"
// 方式一:ok模式s, ok := i.(string)if ok { fmt.Println(s)}
// 方式二:不安全s := i.(string) // 如果不是string,panic
// 方式三:switchswitch v := i.(type) {case string: fmt.Println("string:", v)case int: fmt.Println("int:", v)default: fmt.Println("unknown")}6.4 组合优于继承
Section titled “6.4 组合优于继承”本节目的:理解Go的组合模式、用嵌入替代继承
Go的组合哲学
Section titled “Go的组合哲学”Go没有继承,只有组合:
type Base struct { Name string Age int}
type Child struct { Base // 组合Base School string // 新增字段}
// 访问组合字段child := Child{ Base: Base{Name: "Alice", Age: 10}, School: "No.1 School",}
fmt.Println(child.Name) // Alicefmt.Println(child.Base.Name) // Alicetype Animal struct { Name string}
func (a *Animal) Speak() { fmt.Println("...")}
type Dog struct { Animal // 组合Animal Breed string}
func (d *Dog) Speak() { fmt.Println("Woof!")}
d := Dog{ Animal: Animal{Name: "Buddy"}, Breed: "Golden Retriever",}
d.Speak() // Woof!d.Name // Buddy(继承自Animal)type Reader interface { Read(p []byte) (n int, err error)}
type Closer interface { Close() error}
// 组合多个接口type ReadCloser interface { Reader Closer}
// 验证var _ ReadCloser = (*os.File)(nil)模拟”继承”的方法
Section titled “模拟”继承”的方法”通过组合和代理,可以实现类似继承的效果:
type Base struct { name string}
func (b *Base) SetName(name string) { b.name = name}
func (b *Base) GetName() string { return b.name}
type Child struct { *Base // 组合(指针) school string}
// Child的方法可以调用Base的方法func (c *Child) GetSchool() string { return c.school}
func main() { c := &Child{ Base: &Base{}, } c.SetName("Alice") // 代理到Base fmt.Println(c.GetName())}- 构造函数替代new:Go没有构造函数,用
NewUser(name string) *User工厂函数代替直接创建 - 指针接收者修改状态:需要修改对象或大结构体用
*User,仅读取用User - 接口小而精确:接口应该有一两个方法,如
io.Reader、io.Writer,不要设计大接口 - 避免nil接口陷阱:空接口
interface{}存放nil指针后不是nil,检测时用== nil不可靠 - 嵌入而非继承:用
struct { *Base }嵌入组合,而非模拟Python的继承
| Python | Go |
|---|---|
class User: | type User struct { } |
self.name | u.Name |
def greet(self): | func (u *User) Greet() |
class Child(Parent): | type Child struct { Parent } |
| 显式继承 | 隐式实现接口 |
| 多继承 | 接口组合 |
| 无 | pointer receiver修改对象 |
Go的设计原则:
- 结构体是数据容器
- 方法属于结构体
- 接口是行为契约
- 组合优于继承
- 定义User结构体,实现Greet方法和SetAge方法(使用指针接收者)
- 创建Writer接口,实现到User结构体
- 用空接口实现一个通用打印函数
- 用类型断言从interface提取string和int
- 创建Person结构体,组合Address结构体,实现完整的继承效果