Go 接口
接口定义
基本语法
package main
import "fmt"
// 定义接口
type Writer interface {
Write([]byte) (int, error)
}
// 接口可以包含多个方法
type Reader interface {
Read([]byte) (int, error)
}
type ReadWriter interface {
Reader // 嵌入接口
Writer // 嵌入接口
}
// 实现接口(隐式实现)
type File struct {
name string
}
// File 实现了 Writer 接口
func (f *File) Write(data []byte) (int, error) {
fmt.Printf("写入文件 %s: %s\n", f.name, string(data))
return len(data), nil
}
// File 实现了 Reader 接口
func (f *File) Read(data []byte) (int, error) {
fmt.Printf("从文件 %s 读取\n", f.name)
copy(data, []byte("文件内容"))
return len("文件内容"), nil
}
func main() {
// File 实现了 ReadWriter 接口
var rw ReadWriter = &File{name: "test.txt"}
data := []byte("Hello, World!")
rw.Write(data) // 写入文件 test.txt: Hello, World!
buffer := make([]byte, 100)
rw.Read(buffer) // 从文件 test.txt 读取
}接口实现
隐式实现
package main
import "fmt"
// 定义接口
type Shape interface {
Area() float64
Perimeter() float64
}
// 实现接口(不需要显式声明)
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return 3.14159 * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * 3.14159 * c.Radius
}
func main() {
// 多态:不同的类型实现同一个接口
var shape Shape
shape = Rectangle{Width: 10, Height: 5}
fmt.Printf("矩形面积: %.2f\n", shape.Area())
shape = Circle{Radius: 5}
fmt.Printf("圆形面积: %.2f\n", shape.Area())
// 接口切片
shapes := []Shape{
Rectangle{Width: 10, Height: 5},
Circle{Radius: 5},
}
for _, s := range shapes {
fmt.Printf("面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())
}
}空接口
package main
import "fmt"
// 空接口:可以表示任何类型
type Any interface{}
func main() {
// 空接口可以存储任何值
var i interface{} = 42
var s interface{} = "hello"
var b interface{} = true
fmt.Printf("整数: %v\n", i)
fmt.Printf("字符串: %v\n", s)
fmt.Printf("布尔: %v\n", b)
// 类型断言
if value, ok := i.(int); ok {
fmt.Printf("i 是整数: %d\n", value)
}
// 类型 switch
switch v := i.(type) {
case int:
fmt.Printf("整数: %d\n", v)
case string:
fmt.Printf("字符串: %s\n", v)
default:
fmt.Printf("未知类型: %T\n", v)
}
// 空接口切片(可以存储不同类型的值)
var values []interface{} = []interface{}{
42,
"hello",
true,
3.14,
}
for _, v := range values {
fmt.Printf("值: %v, 类型: %T\n", v, v)
}
}类型断言
package main
import "fmt"
func main() {
var i interface{} = "hello"
// 类型断言:检查接口值的具体类型
// 方式一:两个返回值
value, ok := i.(string)
if ok {
fmt.Printf("是字符串: %s\n", value)
} else {
fmt.Println("不是字符串")
}
// 方式二:一个返回值(如果类型不匹配会 panic)
// value := i.(string) // 如果 i 不是 string,会 panic
// 类型断言用于接口转换
type Writer interface {
Write([]byte) (int, error)
}
type File struct {
name string
}
func (f *File) Write(data []byte) (int, error) {
fmt.Printf("写入: %s\n", string(data))
return len(data), nil
}
var w Writer = &File{name: "test.txt"}
// 断言为具体类型
if file, ok := w.(*File); ok {
fmt.Printf("文件名称: %s\n", file.name)
}
}接口组合
package main
import "fmt"
// 小接口原则:接口应该尽可能小
type Reader interface {
Read([]byte) (int, error)
}
type Writer interface {
Write([]byte) (int, error)
}
type Closer interface {
Close() error
}
// 组合接口
type ReadWriter interface {
Reader
Writer
}
type ReadWriteCloser interface {
Reader
Writer
Closer
}
// 实现
type File struct {
name string
}
func (f *File) Read(data []byte) (int, error) {
fmt.Println("读取文件")
return 0, nil
}
func (f *File) Write(data []byte) (int, error) {
fmt.Println("写入文件")
return 0, nil
}
func (f *File) Close() error {
fmt.Println("关闭文件")
return nil
}
func main() {
var rwc ReadWriteCloser = &File{name: "test.txt"}
rwc.Read(nil)
rwc.Write(nil)
rwc.Close()
}接口值
package main
import "fmt"
type Shape interface {
Area() float64
}
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func main() {
// 接口值包含两部分:
// 1. 动态类型(具体类型)
// 2. 动态值(具体值)
var s Shape // 接口值为 nil(类型和值都是 nil)
fmt.Println(s == nil) // true
var r *Rectangle = nil
s = r // 接口值不为 nil(类型是 *Rectangle,值是 nil)
fmt.Println(s == nil) // false
// 检查接口值是否为 nil
if s != nil {
fmt.Println("接口值不为 nil")
// 但调用方法会 panic(因为值是 nil)
// fmt.Println(s.Area()) // panic: nil pointer dereference
}
// 正确的 nil 检查
rect := &Rectangle{Width: 10, Height: 5}
s = rect
if s != nil {
fmt.Printf("面积: %.2f\n", s.Area()) // 50.00
}
}常见接口
fmt.Stringer
package main
import "fmt"
type Person struct {
Name string
Age int
}
// 实现 Stringer 接口
func (p Person) String() string {
return fmt.Sprintf("%s (%d 岁)", p.Name, p.Age)
}
func main() {
person := Person{Name: "Alice", Age: 30}
fmt.Println(person) // Alice (30 岁)
// fmt.Println 会自动调用 String() 方法
}error 接口
package main
import (
"errors"
"fmt"
)
// 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 divide(a, b int) (int, error) {
if b == 0 {
return 0, &MyError{
Code: 400,
Message: "除数不能为 0",
}
}
return a / b, nil
}
func main() {
result, err := divide(10, 0)
if err != nil {
fmt.Printf("错误: %v\n", err) // 错误: 错误代码 400: 除数不能为 0
return
}
fmt.Printf("结果: %d\n", result)
// 使用标准库的 errors
err2 := errors.New("这是一个错误")
fmt.Println(err2)
}接口最佳实践
package main
import "fmt"
// 1. 小接口原则:接口应该尽可能小
// 好的接口:只包含必要的方法
type Reader interface {
Read([]byte) (int, error)
}
// 不好的接口:包含太多方法
type BadInterface interface {
Read([]byte) (int, error)
Write([]byte) (int, error)
Close() error
Flush() error
Seek(int64, int) (int64, error)
// ... 太多方法
}
// 2. 接受接口,返回具体类型
// 函数参数使用接口
func ProcessData(r Reader) {
// 处理数据
}
// 函数返回具体类型
func NewFile(name string) *File {
return &File{name: name}
}
// 3. 接口隔离原则
type File struct {
name string
}
func (f *File) Read(data []byte) (int, error) {
return 0, nil
}
func (f *File) Write(data []byte) (int, error) {
return 0, nil
}
func (f *File) Close() error {
return nil
}
// 4. 使用接口实现依赖注入
type Database interface {
Query(string) ([]string, error)
}
type MySQL struct{}
func (m *MySQL) Query(sql string) ([]string, error) {
return []string{"result1", "result2"}, nil
}
type Service struct {
db Database // 依赖接口,而不是具体类型
}
func NewService(db Database) *Service {
return &Service{db: db}
}
func (s *Service) GetData() {
results, _ := s.db.Query("SELECT * FROM users")
fmt.Println(results)
}
func main() {
db := &MySQL{}
service := NewService(db) // 注入依赖
service.GetData()
}总结
Go 接口的特点:
- 隐式实现:不需要显式声明实现接口
- 小接口原则:接口应该尽可能小
- 组合:通过组合小接口创建大接口
- 空接口:可以表示任何类型
- 类型断言:检查接口值的具体类型
- 多态:不同的类型实现同一个接口
接口是 Go 实现多态和依赖注入的核心机制!