Go interface 接口

发布时间: 更新时间: 总字数:1776 阅读时间:4m 作者: 分享 复制网址

Golang interface(接口)是一种对其他类型行为进行抽象的自定义类型。接口定义使用interface标识符,用来声明一系列函数签名(函数名、参数、参数类型、函数返回值)。

介绍

接口定义:

type 接口名 interface {
  函数1(参数 类型, ...) 返回值类型1
  函数2(参数 []类型, ...) 返回值类型2
}

说明:

  • 接口用例实现多态
  • 接口名:符合变量命名规则
  • 函数即接口的行为
  • 声明接口变量只需要定义变量类型为接口名,此时变量的值为 nil
  • 当一个自定义类型实现了接口类型中声明的所有函数时,则该自定义类型可以赋值给接口变量,并使用接口变量调用实现的接口
  • 当两个接口声明同样的函数签名时,则这两个接口完全等价
  • 当接口A的方式是接口B方法的,接口A变量可以赋值给接口B变量,但只能调用B的方法

示例

package main

import "fmt"

type Feature interface {
	Fly(name string) bool
	Swim(name string) bool
}

type Duck struct {
}

// Duck  的相关方法声明为值接受者方法
func (d Duck) Fly(name string) bool {
	fmt.Println(name, "can fly")
	return true
}

func (d Duck) Swim(name string) bool {
	fmt.Println(name, "can swim")
	return true
}

type Swan struct {
}

// Swam 的相关方法声明为指针接收者方法
func (s *Swan) Fly(name string) bool {
	fmt.Println(name, "can fly")
	return true
}

func (s *Swan) Swim(name string) bool {
	fmt.Println(name, "can swim")
	return true
}

type Goose struct {
}

// Fly 该方法声明为值接受者,Go编译器默认声明指针类型的 Fly 方法
func (g Goose) Fly(name string) bool {
	fmt.Println(name, "can fly")
	return true
}

// Swim 该方法声明为指针接受者
func (g *Goose) Swim(name string) bool {
	fmt.Println(name, "can swim")
	return true
}

func doFly(name string, f Feature) {
	f.Fly(name)
}

type JustFly interface {
	Fly(name string) bool
}

func main() {
	var f Feature
	fmt.Printf("%T %#v\n", f, f) // <nil> <nil>

	// 实现多态
	f = Duck{}
	fmt.Printf("%T %#v\n", f, f) // main.Duck main.Duck{}

	f.Fly("tanghaoya")
	f.Swim("tanghaoya")

	f = &Swan{}                  // 只实现指针接收者方法,只能使用指针
	fmt.Printf("%T %#v\n", f, f) // main.Swan main.Swan{}

	f.Fly("tiane")
	f.Swim("tiane")

	// 值接收者方法自动添加指针接收者方法,因此可以使用指针赋值
	f = &Duck{}
	f.Fly("zhouheiya")
	f.Swim("zhouheiya")

	// 接口需要实现全部方法,否则不能称为接口
	//f = Goose{} // Cannot use 'Goose{}' (type Goose) as the type Feature Type does not implement 'Feature' as the 'Swim' method has a pointer receiver
	f = &Goose{}
	f.Fly("dabaie")
	f.Swim("dabaie")

	doFly("zhouhaiya", f)

	// 接口赋值给接口
	var js JustFly = f
	// 需要满足对应接口声明的方法和结构体的属性
	js.Fly("chicken")
	//js.Swim("chicken") 没有该方法

	// 接口断言
	goose := js.(*Goose)                        // 转为指针类型
	fmt.Printf("%T %#v\n", goose, goose)        //*main.Goose &main.Goose{}
	goose, ok := js.(*Goose)                    // 转为指针类型
	fmt.Printf("%T %#v %v\n", goose, goose, ok) //*main.Goose &main.Goose{} true

	swan, ok := js.(*Swan)
	// 类型不匹配,转化失败
	fmt.Printf("%T %#v\n", swan, ok) // *main.Swan false

	f = Duck{}
	js = f
	duck := js.(Duck)                          // 转为值类型
	fmt.Printf("%T %#v\n", duck, duck)         // main.Duck main.Duck{}
	duck, ok = js.(Duck)                       // 带是否转化成功标识
	fmt.Printf("%T %#v %#v\n", duck, duck, ok) // main.Duck main.Duck{} true

	// 类型查询,js.(type) 只能使用在 switch 中
	//switch js.(type) {
	switch v := js.(type) {
	case Duck:
		fmt.Println("duck")
		fmt.Printf("%T\n", v)
	//case Swan: // Impossible type switch case: 'Swan' does not implement 'JustFly'
	//	fmt.Println("swan")
	case *Swan: // Impossible type switch case: 'Swan' does not implement 'JustFly'
		fmt.Println("*swan")
	case Goose:
		fmt.Println("goose")
	default:
		fmt.Println("unknown")
	}
}

下面示例是同一个接口:

// A 和 B 是同一个接口,只是名字不同
type A interface {
	do()
}

type B interface {
	do()
}

断言

断言用来判断一个接口是否可以转化为具体的类型

接口变量.(type)
[类型变量1|*类型变量1] := 接口变量.([类型|*类型])
[类型变量1|*类型变量1], [true|false] := 接口变量.([类型|*类型])
  • 类型查询

断言可以扩展为类型查询语法,只能与 switch 语法一起使用

	switch [v :=] 接口变量.(type) {
	case 类型1:
    ...
  case *类型2:
    ...

接口匿名嵌套 & 匿名接口

接口匿名嵌套:

  • 接口可以嵌套子接口,实现接口的扩展
  • 匿名嵌套的子 interface 中,不能存在多个相同的方法名

匿名接口:

  • 匿名接口是定义变量时将类型指定为接口,并包含函数签名
  • 匿名接口常用于初始化一次接口变量的场景
package main

import "fmt"

type Fly interface {
	Fly(name string) bool
}

type Swim interface {
	Swim(name string) bool
}

type Feature interface {
	//Fly(name string) bool
	//Swim(name string) bool
	// 匿名嵌入,效果等同于上面定义
	Fly
	Swim

	// 接口的其他方法
	do(name string)
	clean()
}

// Duck 实现了 Feature、Swim、Fly 3个接口
type Duck struct {
}

func (d Duck) Fly(name string) bool {
	fmt.Println(name, "can fly")
	return true
}

func (d Duck) Swim(name string) bool {
	fmt.Println(name, "can swim")
	return true
}

func (d Duck) do(name string) {
	d.Fly(name)
	d.Swim(name)
}

func (d Duck) clean() {
	fmt.Println("clean")
}

func main() {
	// 匿名嵌套
	d := Duck{}
	// 游玩好后,延迟清理
	defer d.clean()

	var fly Fly = d
	var swim Swim = d
	var feature Feature = d

	fmt.Printf("%T %#v %T %#v %T %#v\n", fly, fly, swim, swim, feature, feature)
	fly.Fly("tanglaoya")
	swim.Swim("tanglaoya")
	feature.do("tanglaoya")

	// 匿名接口
	var chicken interface {
		Fly(name string) bool
	}

	chicken = fly
	chicken.Fly("xiaoji")

	/* Output:
	   main.Duck main.Duck{} main.Duck main.Duck{} main.Duck main.Duck{}
	   tanglaoya can fly
	   tanglaoya can swim
	   tanglaoya can fly
	   tanglaoya can swim
	   xiaoji can fly
	   clean
	*/
}

空接口

定义:

interface{}
  • 空接口是不包含任何函数签名的接口(默认所有类型均实现了0个结构体方法)
  • 作用:
    • 空接口声明的变量可以接受任何类型变量的赋值
    • 一般使用在函数参数中,接受任意类型参数,结合可变参数,接受任意长度的可变参数
package main

import "fmt"

// 空接口
type Empty interface {
}

// 空结构体
type EmptyStruct struct {
}

// 接收任意类型的可变参数
func test(args ...interface{}) {
	for i, arg := range args {
		fmt.Println(i, ":", arg)
		switch v := arg.(type) {
		case int:
			fmt.Println("int", v)
		case string:
			fmt.Println("string", v)
		case bool:
			fmt.Println("bool", v)
		default:
			fmt.Printf("%T %#v", v, v)
		}
	}
}

func main() {
	var es EmptyStruct
	es = EmptyStruct{}
	var e Empty
	fmt.Printf("%T %#v %T %#v\n", es, es, e, e) // main.EmptyStruct main.EmptyStruct{} <nil> <nil>

	e = "hello"
	fmt.Println(e)

	e = true
	fmt.Println(e)

	e = es
	fmt.Println(e)

	// 使用一般定义为匿名接口
	var v interface{}
	fmt.Printf("%T %#v\n", v, v)
	v = 1
	fmt.Printf("%T %#v\n", v, v)

	// 调用能接受任意类型、任意长度的函数
	test(1, "hello", false, e)
}

  • iface 和 eface 的区别

ifaceeface 都是 Go 中描述接口的底层结构体,区别在于 iface 描述的接口包含方法,而 eface 则是不包含任何方法的空接口:interface{}

Golang 内置接口实现

以下都实现 io.Reader 方法,可以相互赋值:

最新评论
加载中...
Home Archives Categories Tags Statistics