This commit is contained in:
2026-03-14 19:03:12 +08:00
parent 2805379586
commit 6d86d62825
34 changed files with 3213 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
# 03-embedding - 组合与类型断言
## 知识点讲解
### 结构体嵌入(组合)
```go
type Address struct {
City string
Street string
}
type Person struct {
Name string
Address Address // 嵌套结构体
}
type Employee struct {
Person // 匿名嵌入
EmployeeID int
}
emp := Employee{
Person: Person{Name: "张三"},
}
fmt.Println(emp.Name) // 可以直接访问嵌入的字段
```
### 接口嵌入
```go
type Reader interface {
Read() string
}
type Writer interface {
Write(s string)
}
type ReadWriter interface {
Reader
Writer // 接口组合
}
```
### 类型断言
```go
var i interface{} = "hello"
// 方式1:标准断言
s := i.(string)
// 方式2:带检查
s, ok := i.(string)
if !ok {
fmt.Println("类型断言失败")
}
```
### 类型选择
```go
func printValue(v interface{}) {
switch v.(type) {
case int:
fmt.Println("整数:", v)
case string:
fmt.Println("字符串:", v)
default:
fmt.Println("未知类型")
}
}
```
### 重要提示
- Go使用组合而非继承
- 结构体嵌入可以实现类似继承的效果
- 类型断言若失败会panic,需谨慎使用
- 类型选择用于处理多种类型
## 学习目标
完成本练习后,你将:
- ✅ 掌握结构体嵌入的使用
- ✅ 理解接口嵌入的概念
- ✅ 学会使用类型断言
- ✅ 掌握类型选择的使用
## 练习说明
打开 `main.go`,根据注释提示填充代码,完成后运行 `go test -v` 验证答案。
+182
View File
@@ -0,0 +1,182 @@
package main
import "fmt"
type Address struct {
City string
Street string
}
type Person struct {
Name string
Age int
Address Address
}
type Employee struct {
Person
EmployeeID int
Department string
}
type Employee2 struct {
____
Salary float64
}
type Reader interface {
Read() string
}
type Writer interface {
Write(s string)
}
type File struct {
Name string
Content string
}
func (f File) Read() string {
return f.___
}
func (f *File) Write(s string) {
f.Content = ____
}
type ReadWriter interface {
____
____
}
func exercise1() string {
fmt.Println("=== 练习1:结构体嵌入 ===")
emp := Employee{
Person: Person{
Name: "张三",
Age: 30,
},
EmployeeID: 1001,
Department: "技术部",
}
fmt.Printf("员工姓名: %s, 年龄: %d\n", ____, emp.___)
return ____
}
func exercise2() string {
fmt.Println("=== 练习2:多层嵌入 ===")
address := Address{City: "北京", Street: "长安街"}
emp2 := Employee2{
Person: Person{
Name: "李四",
Address: ____,
},
Salary: 10000.0,
}
fmt.Printf("员工: %s, 城市: %s\n", emp2.___, emp2.___.City)
return ____
}
func exercise3() string {
fmt.Println("=== 练习3:接口实现 ===")
file := File{Name: "test.txt", Content: "初始内容"}
// 使用Read方法
content := file.____()
fmt.Printf("读取内容: %s\n", content)
// 使用Write方法
file.____("新内容")
fmt.Printf("写入后内容: %s\n", file.___)
return ___
}
func exercise4() string {
fmt.Println("=== 练习4:接口组合 ===")
file := &File{Name: "data.txt", "原始数据"}
var rw _____
rw.Write("修改后的数据")
result := rw.Read()
fmt.Printf("读写结果: %s\n", result)
return ____
}
func exercise5() string {
fmt.Println("=== 练习5:类型断言 ===")
var data interface{} = "Hello, Go!"
// 尝试断言为string
str, ok := data.____
if ok {
fmt.Printf("类型断言成功: %s\n", ____)
}
// 尝试断言为int
_, ok = data.____
if !ok {
fmt.Println("类型断言失败: 不是int类型")
}
return "类型断言演示完成"
}
func exercise6() string {
fmt.Println("=== 练习6:类型选择 ===")
values := []interface{}{10, "hello", 3.14, true}
for _, v := range ____ {
switch v.(type) {
case int:
fmt.Printf("整数: %d\n", ____)
case string:
fmt.Printf("字符串: %s\n", ____)
case float64:
fmt.Printf("浮点数: %.2f\n", ____)
case bool:
fmt.Printf("布尔值: %t\n", ____)
default:
fmt.Println("未知类型")
}
}
return "类型选择演示完成"
}
func main() {
fmt.Println("=== 组合与类型断言 练习 ===\n")
result1 := exercise1()
fmt.Printf("练习1结果: %s\n", result1)
result2 := exercise2()
fmt.Printf("练习2结果: %s\n", result2)
result3 := exercise3()
fmt.Printf("练习3结果: %s\n", result3)
result4 := exercise4()
fmt.Printf("练习4结果: %s\n", result4)
result5 := exercise5()
fmt.Printf("练习5结果: %s\n", result5)
result6 := exercise6()
fmt.Printf("练习6结果: %s\n", result6)
}
+85
View File
@@ -0,0 +1,85 @@
package main
import (
"bytes"
"os"
"strings"
"testing"
)
func captureOutput(f func()) string {
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
f()
w.Close()
os.Stdout = old
var buf bytes.Buffer
buf.ReadFrom(r)
return buf.String()
}
func TestExercise1(t *testing.T) {
result := exercise1()
if result != "张三" {
t.Errorf("期望 '张三', 实际: %s", result)
}
}
func TestExercise2(t *testing.T) {
result := exercise2()
if result != "李四" {
t.Errorf("期望 '李四', 实际: %s", result)
}
}
func TestExercise3(t *testing.T) {
result := exercise3()
if result != "新内容" {
t.Errorf("期望 '新内容', 实际: %s", result)
}
}
func TestExercise4(t *testing.T) {
result := exercise4()
if result != "修改后的数据" {
t.Errorf("期望 '修改后的数据', 实际: %s", result)
}
}
func TestExercise5(t *testing.T) {
result := exercise5()
if result != "类型断言演示完成" {
t.Errorf("期望 '类型断言演示完成', 实际: %s", result)
}
}
func TestExercise6(t *testing.T) {
result := exercise6()
if result != "类型选择演示完成" {
t.Errorf("期望 '类型选择演示完成', 实际: %s", result)
}
}
func TestMainFunction(t *testing.T) {
output := captureOutput(main)
requiredSections := []string{
"=== 组合与类型断言 练习 ===",
"练习1:结构体嵌入",
"练习2:多层嵌入",
"练习3:接口实现",
"练习4:接口组合",
"练习5:类型断言",
"练习6:类型选择",
}
for _, section := range requiredSections {
if !strings.Contains(output, section) {
t.Errorf("期望输出包含 '%s'", section)
}
}
}