欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Go语言接口——interface

程序员文章站 2022-06-14 10:58:45
...

Go语言的Interface接口是一种神奇的特性。Interface包括了一组方法,同时也是一种类型。Interface支持"鸭子类型"(Duck
Typing),只要能做所有鸭子能做的事,就认为它是个鸭子;只要实现了接口的所有方法,就是这个Interface的类型。

type Animal interface {
    Speak() string
}

type Dog struct {
}

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

type Cat struct {
}

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

type Llama struct {
}

func (l Llama) Speak() string {
    return "?????"
}

type JavaProgrammer struct {
}

func (j JavaProgrammer) Speak() string {
    return "Design patterns!"
}

func main() {
    animals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}
    for _, animal := range animals {
        fmt.Println(animal.Speak())
    }
}
1. 空接口 interface{}

interface{}作为类型时可以看做是一个实现了0个方法的匿名接口,任何类型都至少实现了0个方法,因此任何类型都可以实现了interface{}接口。有这样一个有趣的错误的例子。

// 这是错误的
package main

import (
    "fmt"
)

func PrintAll(vals []interface{}) {
    for _, val := range vals {
        fmt.Println(val)
    }
}

func main() {
    names := []string{"stanley", "david", "oscar"}
    PrintAll(names)
}

因为[]interface{}类型是一种普通类型,即元素为interface{}的切片类型,而不是一个接口。如果要转换的话,需要对每个元素进行类型转换,代码如下:

package main

import (
    "fmt"
)

func PrintAll(vals []interface{}) {
    for _, val := range vals {
        fmt.Println(val)
    }
}

func main() {
    names := []string{"stanley", "david", "oscar"}
    vals := make([]interface{}, len(names))
    for i, v := range names {
        vals[i] = v
    }
    PrintAll(vals)
}
2. 接口和指针

如果第一个例子的Cat的speak方法做一个改成如下的代码:

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

再运行程序就会报错。
在go语言中,如果是指针作为receiver,当使用值作为该接口的实例调用会出现编译错。而相反的,使用值作为receiver,使用指针作为接口实例调用没有问题。

为什么呢?
因为通过一个指针,就可以访问这个类型作为receiver的所有方法。但是返回来,给你一个值,却不能访问这个值的指针类型的方法。
在进一步解释就是: Go语言的所有传递都是值传递的。在将Cat转换为Animal的Interface的时候,传递的是Cat的值拷贝,取不到原来的指针,此时取指针的话,取到的就不是原来的Cat的指针,就失去了以指针作为reciever的意义了。而反过来,通过一个指针的值,可以找到唯一的一个Cat对象,所以是可以转换的。
如果了解了,go语言如何实现interface的,interface的数据结构分为两个部分:类型和方法列表,原来的数据本身。就更好理解了。

3. 接口的类型转换

见我的另一篇《Go语言类型转换和类型断言》https://www.jianshu.com/p/bd2acab2a8e9

4. Embedding(嵌入)

Go中没有继承的概念,用Embedding来取代,实现方式为接口的匿名成员。
见《Go匿名成员》 https://www.jianshu.com/p/76eac0b8d563

参考:

  1. http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go
  2. https://research.swtch.com/interfaces