接口
接口是Go的核心特性,实现了多态和解耦。和Java最大的区别:隐式实现!这个设计简直太棒了,我第一次接触的时候就被惊艳到了。不需要显式声明implements,只要实现了方法就自动满足接口,这种鸭子类型的设计让代码更加灵活!
什么是接口?
接口定义了一组方法签名。任何类型只要实现了这些方法,就自动实现了该接口。
// 定义接口
type Speaker interface {
Speak() string
}
// Dog实现了Speaker接口(隐式!无需声明)
type Dog struct {
Name string
}
func (d Dog) Speak() string {
return "汪汪!"
}
// Cat也实现了Speaker接口
type Cat struct {
Name string
}
func (c Cat) Speak() string {
return "喵喵!"
}
Java vs Go
// Java - 显式实现
public class Dog implements Speaker {
@Override
public String speak() { return "汪汪!"; }
}
// Go - 隐式实现(只要有Speak方法就自动实现)
type Dog struct{ Name string }
func (d Dog) Speak() string { return "汪汪!" }
// 无需写 implements!
接口的使用
// 接口类型的变量可以保存任何实现了该接口的值
var s Speaker
s = Dog{Name: "旺财"}
fmt.Println(s.Speak()) // 汪汪!
s = Cat{Name: "咪咪"}
fmt.Println(s.Speak()) // 喵喵!
多态函数
// 接受任何Speaker
func MakeSound(s Speaker) {
fmt.Println(s.Speak())
}
func main() {
dog := Dog{Name: "旺财"}
cat := Cat{Name: "咪咪"}
MakeSound(dog) // 汪汪!
MakeSound(cat) // 喵喵!
// Speaker切片
animals := []Speaker{dog, cat}
for _, animal := range animals {
MakeSound(animal)
}
}
接口的定义规范
// 接口命名通常以 -er 结尾
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type Stringer interface {
String() string
}
// 多个方法的接口
type ReadWriter interface {
Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
}
接口组合
接口可以嵌入其他接口:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
// 组合接口
type ReadWriter interface {
Reader // 嵌入Reader
Writer // 嵌入Writer
}
// 等价于
type ReadWriter interface {
Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
}
空接口 interface{}
空接口没有任何方法,任何类型都实现了空接口。
// 空接口可以接收任何值
var anything interface{}
anything = 42
anything = "hello"
anything = []int{1, 2, 3}
anything = struct{ Name string }{"张三"}
// 函数接收任意类型
func PrintAnything(v interface{}) {
fmt.Println(v)
}
PrintAnything(42)
PrintAnything("hello")
PrintAnything([]int{1, 2, 3})
Go 1.18+ 推荐使用 any
any 是 interface{} 的别名,更简洁:
var anything any = 42
类型断言
从接口类型获取具体类型:
var s Speaker = Dog{Name: "旺财"}
// 类型断言
dog := s.(Dog)
fmt.Println(dog.Name) // 旺财
// 安全的类型断言(推荐!)
dog, ok := s.(Dog)
if ok {
fmt.Println("是狗:", dog.Name)
} else {
fmt.Println("不是狗")
}
// 如果断言失败会panic
// cat := s.(Cat) // panic!
类型选择(Type Switch)
处理多种类型:
func describe(v interface{}) {
switch val := v.(type) {
case int:
fmt.Printf("整数: %d\n", val)
case string:
fmt.Printf("字符串: %s\n", val)
case bool:
fmt.Printf("布尔值: %t\n", val)
case []int:
fmt.Printf("整数切片: %v\n", val)
default:
fmt.Printf("未知类型: %T\n", val)
}
}
func main() {
describe(42) // 整数: 42
describe("hello") // 字符串: hello
describe(true) // 布尔值: true
describe([]int{1,2}) // 整数切片: [1 2]
describe(3.14) // 未知类型: float64
}
接口的零值
接口的零值是 nil,调用nil接口的方法会panic。
var s Speaker // nil
// s.Speak() // panic: nil pointer dereference
if s != nil {
s.Speak()
}
nil接口 vs 接口包含nil值
这是Go接口的一个坑:
type Speaker interface {
Speak() string
}
type Dog struct{}
func (d *Dog) Speak() string { return "汪!" }
func main() {
var s Speaker // nil接口
fmt.Println(s == nil) // true
var d *Dog // nil指针
s = d // 接口不是nil了,但包含nil值
fmt.Println(s == nil) // false(注意!)
// 调用方法会根据实现而定
// s.Speak() // 如果Speak方法不处理nil,可能panic
}
常用标准库接口
Stringer
// fmt包的Stringer接口
type Stringer interface {
String() string
}
type Person struct {
Name string
Age int
}
// 实现Stringer接口
func (p Person) String() string {
return fmt.Sprintf("%s (%d岁)", p.Name, p.Age)
}
func main() {
p := Person{Name: "张三", Age: 25}
fmt.Println(p) // 张三 (25岁)
}
error
// 内置的error接口
type error interface {
Error() string
}
// 自定义错误
type MyError struct {
Code int
Message string
}
func (e MyError) Error() string {
return fmt.Sprintf("[%d] %s", e.Code, e.Message)
}
func doSomething() error {
return MyError{Code: 404, Message: "未找到"}
}
io.Reader 和 io.Writer
import "io"
// 任何实现Read方法的类型都可以作为Reader
type Reader interface {
Read(p []byte) (n int, err error)
}
// 任何实现Write方法的类型都可以作为Writer
type Writer interface {
Write(p []byte) (n int, err error)
}
// 示例:复制数据
func Copy(dst io.Writer, src io.Reader) (int64, error) {
// 从src读取,写入dst
}
// 可以用于文件、网络、缓冲区等
sort.Interface
import "sort"
// 实现这个接口就可以排序
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
// 示例
type Person struct {
Name string
Age int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func main() {
people := []Person{
{"张三", 25},
{"李四", 30},
{"王五", 20},
}
sort.Sort(ByAge(people))
// [{王五 20} {张三 25} {李四 30}]
}
实战案例:支付系统
package main
import (
"errors"
"fmt"
"time"
)
// 支付接口
type PaymentMethod interface {
Pay(amount float64) error
GetName() string
}
// 支付记录
type PaymentRecord struct {
Method string
Amount float64
Status string
Timestamp time.Time
}
// ========== 微信支付 ==========
type WeChatPay struct {
UserID string
Balance float64
}
func (w *WeChatPay) Pay(amount float64) error {
if amount > w.Balance {
return errors.New("微信余额不足")
}
w.Balance -= amount
fmt.Printf("💚 微信支付成功: ¥%.2f\n", amount)
return nil
}
func (w *WeChatPay) GetName() string {
return "微信支付"
}
// ========== 支付宝 ==========
type AliPay struct {
UserID string
Balance float64
}
func (a *AliPay) Pay(amount float64) error {
if amount > a.Balance {
return errors.New("支付宝余额不足")
}
a.Balance -= amount
fmt.Printf("💙 支付宝支付成功: ¥%.2f\n", amount)
return nil
}
func (a *AliPay) GetName() string {
return "支付宝"
}
// ========== 银行卡 ==========
type BankCard struct {
CardNo string
BankName string
Balance float64
}
func (b *BankCard) Pay(amount float64) error {
if amount > b.Balance {
return errors.New("银行卡余额不足")
}
b.Balance -= amount
fmt.Printf("💳 %s银行卡支付成功: ¥%.2f\n", b.BankName, amount)
return nil
}
func (b *BankCard) GetName() string {
return b.BankName + "银行卡"
}
// ========== 支付处理器 ==========
type PaymentProcessor struct {
records []PaymentRecord
}
// 处理支付
func (p *PaymentProcessor) Process(method PaymentMethod, amount float64) error {
record := PaymentRecord{
Method: method.GetName(),
Amount: amount,
Timestamp: time.Now(),
}
if err := method.Pay(amount); err != nil {
record.Status = "失败: " + err.Error()
p.records = append(p.records, record)
return err
}
record.Status = "成功"
p.records = append(p.records, record)
return nil
}
// 打印支付记录
func (p *PaymentProcessor) PrintRecords() {
fmt.Println("\n📋 支付记录")
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
for i, r := range p.records {
fmt.Printf("%d. [%s] %s ¥%.2f - %s\n",
i+1,
r.Timestamp.Format("15:04:05"),
r.Method,
r.Amount,
r.Status,
)
}
}
func main() {
// 创建支付方式
wechat := &WeChatPay{UserID: "wx123", Balance: 1000}
alipay := &AliPay{UserID: "ali456", Balance: 500}
bank := &BankCard{CardNo: "6222xxxx", BankName: "工商", Balance: 10000}
// 创建支付处理器
processor := &PaymentProcessor{}
// 处理支付
processor.Process(wechat, 88.8)
processor.Process(alipay, 299)
processor.Process(bank, 1999)
processor.Process(alipay, 500) // 余额不足
// 打印记录
processor.PrintRecords()
// 打印余额
fmt.Println("\n💰 账户余额")
fmt.Printf(" 微信: ¥%.2f\n", wechat.Balance)
fmt.Printf(" 支付宝: ¥%.2f\n", alipay.Balance)
fmt.Printf(" 银行卡: ¥%.2f\n", bank.Balance)
}
输出:
💚 微信支付成功: ¥88.80
💙 支付宝支付成功: ¥299.00
💳 工商银行卡支付成功: ¥1999.00
📋 支付记录
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. [10:30:00] 微信支付 ¥88.80 - 成功
2. [10:30:00] 支付宝 ¥299.00 - 成功
3. [10:30:00] 工商银行卡 ¥1999.00 - 成功
4. [10:30:00] 支付宝 ¥500.00 - 失败: 支付宝余额不足
💰 账户余额
微信: ¥911.20
支付宝: ¥201.00
银行卡: ¥8001.00
实战案例:数据库抽象层
package main
import (
"fmt"
"time"
)
// 数据库接口
type Database interface {
Connect() error
Close() error
Query(sql string) ([]map[string]interface{}, error)
Execute(sql string) (int64, error)
}
// ========== MySQL实现 ==========
type MySQL struct {
Host string
Port int
User string
Password string
DBName string
connected bool
}
func (m *MySQL) Connect() error {
fmt.Printf("🔌 连接MySQL: %s:%d/%s\n", m.Host, m.Port, m.DBName)
m.connected = true
return nil
}
func (m *MySQL) Close() error {
fmt.Println("👋 关闭MySQL连接")
m.connected = false
return nil
}
func (m *MySQL) Query(sql string) ([]map[string]interface{}, error) {
fmt.Printf("📖 MySQL查询: %s\n", sql)
// 模拟返回数据
return []map[string]interface{}{
{"id": 1, "name": "张三"},
{"id": 2, "name": "李四"},
}, nil
}
func (m *MySQL) Execute(sql string) (int64, error) {
fmt.Printf("✏️ MySQL执行: %s\n", sql)
return 1, nil
}
// ========== PostgreSQL实现 ==========
type PostgreSQL struct {
Host string
Port int
User string
Password string
DBName string
connected bool
}
func (p *PostgreSQL) Connect() error {
fmt.Printf("🔌 连接PostgreSQL: %s:%d/%s\n", p.Host, p.Port, p.DBName)
p.connected = true
return nil
}
func (p *PostgreSQL) Close() error {
fmt.Println("👋 关闭PostgreSQL连接")
p.connected = false
return nil
}
func (p *PostgreSQL) Query(sql string) ([]map[string]interface{}, error) {
fmt.Printf("📖 PostgreSQL查询: %s\n", sql)
return []map[string]interface{}{
{"id": 1, "name": "王五"},
}, nil
}
func (p *PostgreSQL) Execute(sql string) (int64, error) {
fmt.Printf("✏️ PostgreSQL执行: %s\n", sql)
return 1, nil
}
// ========== 数据库服务 ==========
type DBService struct {
db Database
}
func NewDBService(db Database) *DBService {
return &DBService{db: db}
}
func (s *DBService) Init() error {
return s.db.Connect()
}
func (s *DBService) Shutdown() error {
return s.db.Close()
}
func (s *DBService) GetUsers() ([]map[string]interface{}, error) {
return s.db.Query("SELECT * FROM users")
}
func (s *DBService) CreateUser(name string) error {
sql := fmt.Sprintf("INSERT INTO users (name, created_at) VALUES ('%s', '%s')",
name, time.Now().Format("2006-01-02"))
_, err := s.db.Execute(sql)
return err
}
func main() {
// 使用MySQL
fmt.Println("===== 使用 MySQL =====")
mysqlDB := &MySQL{
Host: "localhost",
Port: 3306,
DBName: "myapp",
}
service := NewDBService(mysqlDB)
service.Init()
service.GetUsers()
service.CreateUser("赵六")
service.Shutdown()
fmt.Println()
// 切换到PostgreSQL - 业务代码无需修改!
fmt.Println("===== 使用 PostgreSQL =====")
pgDB := &PostgreSQL{
Host: "localhost",
Port: 5432,
DBName: "myapp",
}
service = NewDBService(pgDB)
service.Init()
service.GetUsers()
service.CreateUser("钱七")
service.Shutdown()
}
接口设计原则
1. 接口要小
// ❌ 太大的接口
type Repository interface {
Find(id int) (*User, error)
FindAll() ([]*User, error)
Create(user *User) error
Update(user *User) error
Delete(id int) error
Search(query string) ([]*User, error)
// ... 更多方法
}
// ✅ 小接口,按需组合
type Reader interface {
Find(id int) (*User, error)
}
type Writer interface {
Create(user *User) error
Update(user *User) error
}
type Deleter interface {
Delete(id int) error
}
type ReadWriter interface {
Reader
Writer
}
2. 接口由使用者定义
// 在使用的包中定义接口,而不是实现的包中
// 这样可以避免不必要的依赖
// user_service.go
type UserRepository interface {
GetByID(id int) (*User, error)
}
type UserService struct {
repo UserRepository
}
3. 返回具体类型,接收接口类型
// ✅ 好的设计
func NewMySQL() *MySQL { ... } // 返回具体类型
func DoSomething(db Database) { ... } // 接收接口
// ❌ 不推荐
func NewDatabase() Database { ... } // 返回接口,隐藏了实现
练习
- 定义一个
Shape接口,包含Area()和Perimeter()方法,实现Circle和Rectangle - 定义一个
Logger接口,实现控制台日志和文件日志 - 实现一个简单的插件系统,定义
Plugin接口
参考答案
package main
import (
"fmt"
"math"
"time"
)
// 1. Shape接口
type Shape interface {
Area() float64
Perimeter() float64
}
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
}
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)
}
// 2. Logger接口
type Logger interface {
Log(message string)
Error(message string)
}
type ConsoleLogger struct{}
func (l ConsoleLogger) Log(message string) {
fmt.Printf("[LOG] %s: %s\n", time.Now().Format("15:04:05"), message)
}
func (l ConsoleLogger) Error(message string) {
fmt.Printf("[ERR] %s: %s\n", time.Now().Format("15:04:05"), message)
}
type FileLogger struct {
Filename string
}
func (l FileLogger) Log(message string) {
fmt.Printf("[FILE:%s] LOG: %s\n", l.Filename, message)
}
func (l FileLogger) Error(message string) {
fmt.Printf("[FILE:%s] ERR: %s\n", l.Filename, message)
}
// 3. Plugin接口
type Plugin interface {
Name() string
Init() error
Execute() error
Cleanup()
}
type HelloPlugin struct{}
func (p HelloPlugin) Name() string { return "HelloPlugin" }
func (p HelloPlugin) Init() error {
fmt.Println("HelloPlugin初始化")
return nil
}
func (p HelloPlugin) Execute() error {
fmt.Println("Hello from plugin!")
return nil
}
func (p HelloPlugin) Cleanup() {
fmt.Println("HelloPlugin清理")
}
func main() {
// 1. 测试Shape
shapes := []Shape{
Circle{Radius: 5},
Rectangle{Width: 10, Height: 5},
}
for _, s := range shapes {
fmt.Printf("面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())
}
// 2. 测试Logger
var logger Logger = ConsoleLogger{}
logger.Log("这是一条日志")
logger = FileLogger{Filename: "app.log"}
logger.Error("这是一条错误")
// 3. 测试Plugin
plugin := HelloPlugin{}
plugin.Init()
plugin.Execute()
plugin.Cleanup()
}
接口是Go解耦的关键,下一节学习错误处理!
