Skip to main content

如何使用 Go interface

Go

前言

记录自己对 Go interface的理解与应用。

interface的定义

  • interface是一组方法的集合,任何类型只要实现了 interface 中方法集合,那么就属于这个类型;
  • Go interface使用的是一种鸭子类型的方式让动态类型成为了可能;

假设定义一个 Engine 接口:

package main

import "fmt"

// 定义Engine接口
type Engine interface {
	Start()
	Stop()
}

type Car struct{}

type Airplane struct{}

// Car的实现,c接收者接收了Start方法
func (c Car) Start() {
	fmt.Println("Car is running.")
}

func (c Car) Stop() {
	fmt.Println("Car stop.")
}

func (a Airplane) Start() {
	fmt.Println("Airplane is running.")
}

func (a Airplane) Stop() {
	fmt.Println("Airplane stop.")
}

func startEngine(e Engine) {
	e.Start()
}

func stopEngine(e Engine) {
	e.Stop()
}

func main() {

	var car Car
	var airplane Airplane

	startEngine(car)      // Car is running.
	startEngine(airplane) // Airplane is running.

	stopEngine(car)       // Car stop.
	stopEngine(airplane)  // Airplane stop.

}

interface应用

interface{}它不是泛型,它是一个接口,这个接口可以适配任何类型;

nil interface

  • nil interface的官方解释:Interface values with nil underlying values;
  • 只声明没赋值的interface 是 nil interface,value 和 type 都是 nil;
  • 只要赋值了,即使赋了一个值为 nil 类型,也不再是 nil interface;
package main

import "fmt"

type I interface {
	M()
}

type T struct {
	S string
}

func (t *T) M() {
	if t == nil {
		fmt.Println("<nil>")
		return
	}
	fmt.Println(t.S)
}

func describe(i I) {
	fmt.Printf("(%v, %T)\n", i, i)
}

func main() {
	var i I
	var t *T

	i = t
	describe(t)
	i.M()

	i = &T{"hello"}
	describe(i)
	i.M()
}

output

(<nil>, *main.T)
<nil>
(&{hello}, *main.T)
hello

empty interface 空接口

interface{}叫做空接口,空接口表示包含了0个方法的集合,由于任何类型都至少实现了0个方法,所以空接口可以承接任意类型;

  • 空接口不用指定类型变量,并且类型可变:
package main

import "fmt"

type I interface{}

func main() {
    var i I
    i = 42      //这个时候i就是int类型
    fmt.Printf("%v,%T\n", i, i)
    i = "hello" //这个时候i就是string类型
    fmt.Printf("%v,%T\n", i, i)
}

output

42,int
hello,string

struct 中嵌套 interface

struct中嵌套interface,这种情况比较特殊,struct中嵌套interface之后,struct会自动获得接口规定的方法:

package main

import "fmt"

type I interface {
	M()
}

type T struct {
	I
}

type K struct {
	s string
}

func (k *K) M() {
	fmt.Println(k.s)
}

func newT(s string) *T {

	return &T{&K{s}}
}

func main() {

	t := newT("Hello")
	t.M()
}

output

hello