ZetCode

Golang interface 关键字

最后修改于 2025 年 5 月 7 日

本教程解释了如何在 Go 中使用 interface 关键字。我们将通过多态的实际示例涵盖接口基础知识。

interface 类型定义了一组方法签名。任何实现了这些方法的类型都隐式地满足该接口。

在 Go 中,接口实现了多态和灵活的代码设计。如果不同类型实现了相同的接口,则可以统一对待它们。

基本接口定义

一个简单的接口定义了没有实现的方法签名。此示例显示了一个基本接口及其实现。

basic_interface.go
package main

import "fmt"

type Speaker interface {
    Speak() string
}

type Dog struct{}

func (d Dog) Speak() string {
    return "Woof!"
}

type Cat struct{}

func (c Cat) Speak() string {
    return "Meow!"
}

func main() {
    animals := []Speaker{Dog{}, Cat{}}
    
    for _, animal := range animals {
        fmt.Println(animal.Speak())
    }
}

Speaker 接口需要一个 Speak 方法。DogCat 都实现了它,从而可以进行多态使用。

空接口

空接口 interface{} 没有方法。所有类型都实现了它,这使得它对于泛型函数很有用。

empty_interface.go
package main

import "fmt"

func describe(i interface{}) {
    fmt.Printf("Type: %T, Value: %v\n", i, i)
}

func main() {
    describe(42)
    describe("hello")
    describe(3.14)
    describe([]int{1, 2, 3})
}

describe 函数通过空接口接受任何类型。它打印传递给它的任何内容的类型和值。

接口组合

接口可以嵌入其他接口以创建更复杂的契约。此示例组合了多个接口。

composite_interface.go
package main

import "fmt"

type Walker interface {
    Walk()
}

type Runner interface {
    Run()
}

type Athlete interface {
    Walker
    Runner
}

type Human struct{}

func (h Human) Walk() {
    fmt.Println("Human walking")
}

func (h Human) Run() {
    fmt.Println("Human running")
}

func main() {
    var athlete Athlete = Human{}
    athlete.Walk()
    athlete.Run()
}

Athlete 结合了 WalkerRunnerHuman 实现了这两种方法,满足了复合接口。

类型断言

类型断言检查接口值是否保存了特定类型。此示例演示了安全的类型检查。

type_assertion.go
package main

import "fmt"

func checkType(i interface{}) {
    if s, ok := i.(string); ok {
        fmt.Printf("It's a string: %s\n", s)
    } else if n, ok := i.(int); ok {
        fmt.Printf("It's an int: %d\n", n)
    } else {
        fmt.Printf("Unknown type: %T\n", i)
    }
}

func main() {
    checkType("hello")
    checkType(42)
    checkType(3.14)
}

checkType 函数使用类型断言来确定接口值的底层类型,并妥善处理每种情况。

类型开关

类型开关简化了具有多个情况的类型断言。此示例显示了一种更清晰地处理不同类型的方法。

type_switch.go
package main

import "fmt"

func process(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Integer: %d\n", v)
    case string:
        fmt.Printf("String: %s\n", v)
    case bool:
        fmt.Printf("Boolean: %v\n", v)
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}

func main() {
    process(42)
    process("gopher")
    process(true)
    process(3.14)
}

类型开关语法 i.(type) 以清晰、可读的方式检查多种可能的类型。每种情况处理一种不同的类型。

实际示例:数据库抽象

接口在创建抽象方面非常强大。此示例展示了一个简单的数据库接口,具有多种实现。

database_interface.go
package main

import "fmt"

type Database interface {
    Connect() string
    Query(q string) string
}

type MySQL struct{}

func (m MySQL) Connect() string {
    return "MySQL connected"
}

func (m MySQL) Query(q string) string {
    return fmt.Sprintf("MySQL query: %s", q)
}

type PostgreSQL struct{}

func (p PostgreSQL) Connect() string {
    return "PostgreSQL connected"
}

func (p PostgreSQL) Query(q string) string {
    return fmt.Sprintf("PostgreSQL query: %s", q)
}

func main() {
    databases := []Database{MySQL{}, PostgreSQL{}}
    
    for _, db := range databases {
        fmt.Println(db.Connect())
        fmt.Println(db.Query("SELECT * FROM users"))
    }
}

Database 接口定义了通用操作。两种数据库类型都实现了它,允许尽管实现不同但使用方式统一。

接口实现验证

Go 可以在编译时验证类型是否满足接口。此示例显示了显式的接口实现检查。

interface_check.go
package main

import "fmt"

type Writer interface {
    Write([]byte) (int, error)
}

type ConsoleWriter struct{}

func (cw ConsoleWriter) Write(data []byte) (int, error) {
    n, err := fmt.Println(string(data))
    return n, err
}

func main() {
    var w Writer = ConsoleWriter{}
    w.Write([]byte("Hello, interfaces!"))
    
    // Compile-time check
    var _ Writer = (*ConsoleWriter)(nil)
}

var _ Writer = (*ConsoleWriter)(nil) 在编译时验证 ConsoleWriter 是否满足 Writer。这是 Go 中常见的惯用法。

来源

Go 语言规范

本教程通过多态、类型断言和接口组合的实际示例,涵盖了 Go 中的 interface 关键字。

作者

我叫 Jan Bodnar,我是一名充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出所有 Golang 教程