Go语言接口

接口是对行为的规范,Go语言中的接口是一组方法的签名,并且是非侵入式的。

Go的接口是一种突破的设计。

一、接口

接口是对行为的规范,比如接口定义了行为,但是接口中没有具体的实现,需要你对行为进行细节的描述。

go中就更简单了,只有一组方法的名字,没有接收器类型,也就没有一大堆的继承实现树图。

只要一个类型有接口中定义的所有方法,就是实现了该接口,不需要显式的声明接口。

接口声明

type 接口类型名 interface{
    方法名1( 参数列表1 ) 返回值列表1
    方法名2( 参数列表2 ) 返回值列表2
    …
    
}
type FlyS interface {
    //无返回值
	Fly()
	Run()
	walk()
}

接口实现

1、接口的方法与实现接口的类型方法格式一致

2、接口中所有方法均被实现

以上两个条件必须都满足,否则不是对接口的实现。

package class

import "fmt"

type FlyS interface {
	Fly()
	Run()
	walk()
}

type Brid struct {
	Name string
	Color string
}

func ( b Brid)Fly()  {
	fmt.Println(b.Name+"飞起来啦。。。")
}

func ( b Brid)Run()  {
	fmt.Println(b.Name+"跑起来啦。。。")
}

func ( b Brid)walk()  {
	fmt.Println(b.Name+"走起来啦。。。")
}

测试

package main

import (
	"class"
)

func main()  {

	var f class.FlyS

	brid := class.Brid{"Jim", "yellow"}

	brid.Fly()

	f=brid

	f.Fly()

}


Jim飞起来啦。。。
Jim飞起来啦。。。

二、空接口

空接口是接口类型的特殊形式,空接口没有任何方法,因此任何类型都无须实现空接口。从实现的角度看,任何值都满足这个接口的需求。因此空接口类型可以保存任何值,也可以从空接口中取出原值。

提示

空接口类型类似于 java中的Object.
空接口的内部实现保存了对象的类型和指针。使用空接口保存一个数据的过程会比直接用数据对应类型的变量保存稍慢。因此在开发中,应在需要的地方使用空接口,而不是在所有地方使用空接口。

空接口中没有方法,所以他可以看做被任意类型实现了,也可以接收任意类型。

type name interface {
	
}

空接口可接收任意类型的数据,其本身类型就是nil。

{
    var a A

	fmt.Printf("%T",a)//nil

	a=3
	a="Hello"
    
    fmt.Printf("%T",a)//string

}

//空接口
type A interface {

}

很多函数和方法都借助了空接口,比如打印函数。

func Println

func Println(a ...interface{}) (n int, err error)

三、接口嵌套

go中一个类型是可以实现多个接口的。

//接口一
type FlyS interface {
	Fly()
}
//接口二
type Runs interface {
	Run()
}
//接口三
type Walks interface {
	Walk()
} 
//实现类
type Brid struct {
	Name string
}

//实现了三个接口.
func ( b Brid)Fly()  {
	fmt.Println(b.Name+"飞起来啦。。。")
}
func (b Brid)Run()  {
	fmt.Println(b.Name+"跑起来啦")
}
func (b Brid)Walk()  {
	fmt.Println(b.Name+"走起来啦")
}

以上写法有时候可能不方便,比如在接收对象的时候,对接口类型需要熟悉(空接口也提供了断言来判断),go语言提供了接口嵌套。

//嵌套接口
type GoS interface {
	FlyS
	Runs
	Walks
}

//接口一
type FlyS interface {
	Fly()
}
//接口二
type Runs interface {
	Run()
}
//接口三
type Walks interface {
	Walk()
} 
//实现类
type Brid struct {
	Name string
}

//实现了三个接口.
func ( b Brid)Fly()  {
	fmt.Println(b.Name+"飞起来啦。。。")
}
func (b Brid)Run()  {
	fmt.Println(b.Name+"跑起来啦")
}
func (b Brid)Walk()  {
	fmt.Println(b.Name+"走起来啦")
}

我们在主函数中测试一下,嵌套是否有效。

func main()  {

	var g GoS

	brid := class.Brid{"Jim"}
	
    //能接收实现类的对象
	g=brid
	g.Fly()
	g.Run()
	g.Walk()

}
Jim飞起来啦。。。
Jim跑起来啦
Jim走起来啦

四、断言

如果我们反向想要知道这个接口变量里面实际存储的是哪个类型的对象,可以使用断言。

断言就是对类型进行转换来判断类型。

package main

import (
    "fmt"
)

type Animal interface {
    Eat()
    Talk()
}

type Dog struct{

}

func (d *Dog) Eat(){
    fmt.Println("dog eating.....")
}

func (d *Dog) Talk(){
    fmt.Println("dog talking....")
}

type Cat struct{

}

func (c *Cat) Eat(){
    fmt.Println("cat eating.....")
}

func (c *Cat) Talk(){
    fmt.Println("cat talking....")
}

func justify(a Animal){
    // 进行强制转换,如果转换失败则提示错误
    dog,ok := a.(*Dog)
    if !ok{
        fmt.Println("convert to dog failed")
        return
    }
    dog.Eat()
}

func main()  {
    // 分别实例化一个Dog和Cat,并通过justify来进行判断
    d := &Dog{}
    var a Animal
    a = d
    a.Eat()
    justify(a)

    c := &Cat{}
    a = c
    justify(a)
}