Go基础编程 - 10- 接口(interface)

上一篇:通道(Channel)
下一篇:函数(func)


1. 接口

Go语言中接口(interface)是一种类型,一种抽象的类型。

1.1. 接口的定义

Go语言提倡面向接口编程。每个 interface 都有一组方法,这些方法定义了接口的行为。

type 接口名 interface {
    方法名1(参数列表) 返回值列表
    方法名2(参数列表) 返回值列表
   ...
}

// 1.接口名:使用type将接口定义为自定义的类型名。Go语言接口命名习惯以`er`结尾。接口名最好要能突出该接口的类型含义。
// 2.方法名:方法名首字母大写且这个接口类型名首字母也是大写时,该方法可以被接口所在的包(package)之外的代码访问。
// 3.参数列表、返回值列表中的参数变量名可以省略。
  • 接口是一个或多个方法签名的集合
  • 接口只有方法声明,没有实现,没有数据字段
  • 对象赋值给接口时,会发生拷贝,而接口内部存储的是指向这个复制品的指针,既无法修改复制品的状态,也无法获取指针。
  • 接口同样支持匿名字段方法
  • 接口可以匿名嵌入其它接口,或嵌入到结构中
  • 接口调用不会做receiver的自动切换
  • 接口也可实现类似OOP中的多态
  • 空接口可以作为任何类型数据的容器
  • 一个类型可以实现多个接口

1.2. 接口的实现

任何类型的方法集中只要全部“实现”了接口中的方法,就表示它“实现”了该接口,无须在该类型上显式声明实现了哪个接口。

定义一个Sayer接口

type Sayer interface {
    Speak()
}

定义一个Person类型,并实现了Speak方法,该类型实现了Sayer接口。

type Person struct {
    Name string
}

func (p Person) Speak() {
    fmt.Println("Hello, my name is", p.Name)
}

1.3. 接口的变量类型

接口类型变量能够存储所有实现了该接口的实例。

例如:

package main

import "fmt"

type Sayer interface {
    Speak()
}

type Person struct {
    Name string
}

func (p Person) Speak() {
    fmt.Println("Hello, my name is", p.Name)
}

func (p Person) Run() {
    fmt.Println("跑步去了...")
}

func main() {
    var s Sayer
    p := Person{"小强"}

    s = p   // 接口类型变量可以存储实现了该接口的实例
    s.Speak() // Hello, my name is 小强
    p.Run()
    //s.Run() // 但不可使用接口外的方法,报错:s.Run undefined (type Sayer has no field or method Run)
}

1.4. 值接收者和指针接收者实现接口的区别

Go语言指针语法糖:当我们使用指针调用一个方法或访问结构体的字段时,编译器会自动解引用指针,这使得我们可以像直接访问变量一样操作指针。

结构体中,值方法集合不包括指针方法,指针方法集合包括所有方法

1.4.1. 值接收者实现接口

type Sayer interface {
    Speak()
}

type Person struct {
    Name string
}

func (p Person) Speak() {
	fmt.Println("Hello, my name is", p.Name)
}

func main() {
    var s Sayer

    p := Person{"小强"}
    s = p

    p2 := &Person{"梦想家"}
    s = p2
    s.Speak()
}

从上面的代码中我们可以发现,使用值接收者实现接口之后,不管是Person结构体类型的变量p,还是*Person结构体指针类型的变量p2,都可以赋值给该接口变量s。因为Go语言中有对指针类型变量求值的语法糖,p2内部会自动求值*p2。

1.4.2. 指针接收者实现接口

func (p *Person) Speak() {
	fmt.Println("Hello, my name is", p.Name)
}

func main() {
    var s Sayer

    p := Person{"小强"}
    // Person的方法集合,不包括指针方法,因此Person类型实例并未实现Speak方法,也就未实现Sayer接口。
    s = p	// 报错: cannot use p (variable of type Person) as type Sayer in assignment:
            // Person does not implement Sayer (Speak method has pointer receiver)

    p2 := &Person{"梦想家"}
    s = p2
    s.Speak()
}

此时实现Speak接口的是*Person类型,所以不能给s传入Person类型的p,此时s只能存储*Person类型的值。

2. 类型与接口的关系

一个类型可以同时实现多个接口,而接口之间彼此独立,不知道对方的实现。

一个接口的方法,不一定要由一个类型完全实现,接口的方法可以通过在类型中嵌入其它类型或结构体来实现。

// WashingMachine 洗衣机
type WashingMachine interface {
    wash()
    dry()
}

// 甩干器
type dryer struct{}

// 实现WashingMachine接口的dry()方法
func (d dryer) dry() {
    fmt.Println("甩一甩")
}

// 海尔洗衣机
type haier struct {
    dryer //嵌入甩干器,实现接口dry方法
}

// 实现WashingMachine接口的wash()方法
func (h haier) wash() {
    fmt.Println("洗刷刷")
}

3. 接口的嵌套

接口与接口间可以通过嵌套生成新的接口。嵌套得到的接口的使用与普通接口一样。

// Sayer 接口
type Sayer interface {
    say()
}

// Mover 接口
type Mover interface {
    move()
}

// 接口嵌套
type animal interface {
    Sayer
    Mover
}

4. 空接口

空接口是指没有定义任何方法的接口。因此任何类型都实现了空接口,空接口类型的变量可以存储任意类型的变量。

func main() {
    var x interface{}
    s := "string"
    x = s
   
    i := 100
    x = i
  
    b := true
    x = b
}

4.1. 空接口的应用

空接口作为函数的参数(或返回值),实现可以接收任意类型的函数参数(或返回值)

func foo(arg interface{}) {
    fmt.Printf("type:%T,  value:%v\n", arg, arg)
}

func main() {
    foo(1)
    foo("hello")
    foo(true)
    foo(nil)
    foo(map[string]int{"a": 1})
}

空接口作为map的值,实现可以保存任意值的字典

func main() {
    var m map[string]interface{}
    m["name"] = "小强"
    m["age"] = 18
    m["married"] = false

    fmt.Println(m)
}

使用类型断言,获取接口的值
空接口可以存储任意类型的值,获取其存储的具体的值就可以使用类型断言,其语法格式:
【参考基本类型章节:3.4 类型断言】

v, ok := x.(T)
// x:表示类型为interface{}的变量
// T:表示断言x可能的类型
// v:接口值
// ok:布尔值,true表示断言成功,false表示断言失败。可省略,省略后断言失败则产生panic。

func main() {
    var x interface{}
    x = "hello"
    v, ok := x.(string)
    if ok {
        fmt.Println(v)
    } else {
        fmt.Println("类型断言失败")
    }
}
  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值