1. 前言

go语言和其它面向对象编程语言语言不太一样,它本身是没有类和继承的概念,但是她有灵活的接口概念,通过接口同样可以实现很多面向对象的特性。

2. 接口的定义

接口把一组具有共性的方法定义在一起,这些方法是抽象的,不包含实现的代码,由具体的对象来规范方法的细节。接口(interface)是一种抽象的类型。

/* 定义接口 */
type interface_name interface {
   method_1 [return_type]
   ...
   method_n [return_type]
}

/* 定义结构体 */
type struct_name struct {
   /* variables */
}

/* 实现接口方法 */
func (struct_name_variable struct_name) method_n() [return_type] {
   /* 方法实现 */
}

当接口类型名、方法名首字母是大写时,这个方法可以被接口所在的包之外的代码访问。

例子:

package main

import (
    "fmt"
)

// Fruits 接口
type Fruits interface {
    Eat()
}

type Apple struct {
}

func (apple Apple) Eat() {
    fmt.Println("I want to eat Apple")
}

type Banana struct {
}

func (banana Banana) Eat() {
    fmt.Println("I want to eat Banana")
}

func main() {
    var f Fruits

    f = new(Apple)
    f.Eat()

    f = new(Banana)
    f.Eat()

    f = Apple{}
    f.Eat()
}

上面例子,定义了水果 Fruits 接口,接口里有个吃 Eat 的方法,当我们要决定吃什么,只需要分别给Apple和Banana实现Eat方法就实现了 Fruits 接口

输出:

I want to eat Apple
I want to eat Banana
I want to eat Apple

注:只有大于一个以上的具体类型必须以相同的方式进行处理时才需要定义接口。随意定义接口,只会增加不必要的抽象,导致不必要的运行损耗。

3. 分析值接收实现接口

从下面代码中可看出,使用值接收实现接口,不管是结构体类型还是结构体指针类型的变量都是可以赋值给接口变量的。

package main

import (
    "fmt"
)

// Fruits 接口
type Fruits interface {
    Eat()
}

type Apple struct {
}

// 值接收实现接口
func (apple Apple) Eat() {
    fmt.Println("I want to eat Apple")
}

func main() {
    var f Fruits

    f = Apple{}
    f.Eat()

    f = &Apple{}
    f.Eat()
}

输出:

I want to eat Apple
I want to eat Apple

4. 分析指针接受实现接口

从下面代码可以分析出,当使用的是指针接收实现接口时,只能传指针类型的变量给接口。

package main

import (
    "fmt"
)

// Fruits 接口
type Fruits interface {
    Eat()
}

type Apple struct {
}

// 指针接收实现接口
func (apple *Apple) Eat() {
    fmt.Println("I want to eat Apple")
}

func main() {
    var f Fruits

    // 不能接收Apple类型
    //f = Apple{}
    //f.Eat()

    // 可以接收*Apple类型
    f = &Apple{}
    f.Eat()
}

5. 多个类型实现同一接口

go语言可以不同类型实现同一个接口,可看下面代码:

package main

import (
    "fmt"
)

// Fruits 接口
type Fruits interface {
    Eat()
}

type Apple struct {
    Price int64
}

func (apple Apple) Eat() {
    fmt.Printf("I want to eat Apple,its price is %d\n", apple.Price)
}

type Banana struct {
    Date string
}

func (banana Banana) Eat() {
    fmt.Printf("I want to eat Banana,its date is %s\n", banana.Date)
}

func main() {
    var f Fruits
    a := Apple{Price: 18}
    b := Banana{Date: "2021-11-03"}

    f = a
    f.Eat()

    f = b
    f.Eat()
}

6. 一个类型实现多个接口

一个类型也可实现多个接口,接口之间彼此独立,不会影响各自的实现。

例如,苹果可以拿来吃,也可以拿来送人:

package main

import (
    "fmt"
)

// Fruits 接口
type Fruits interface {
    Eat()
}

// Gift 接口
type Gift interface {
    Give()
}

type Apple struct {
    Name string
    Price int64
}

func (apple Apple) Eat() {
    fmt.Printf("I want to eat Apple,its price is %d\n", apple.Price)
}

func (apple Apple) Give() {
    fmt.Printf("I want to give you %s\n", apple.Name)
}

func main() {
    var f Fruits
    var g Gift
    a := Apple{Name: "apples", Price: 18}

    f = a
    g = a

    f.Eat()
    g.Give()
}

输出:

I want to eat Apple,its price is 18
I want to give you apples

7. 接口嵌套接口

一个接口可以包含一个或多个其他的接口。

package main

import (
    "fmt"
)

// Fruits 接口
type Fruits interface {
    Eat()
}

// Gift 接口
type Gift interface {
    Give()
}

// Person 接口
type Person interface {
    Fruits
    Gift
}

type Apple struct {
    Name string
    Price int64
}

func (a Apple) Eat() {
    fmt.Printf("I want to eat Apple,its price is %d\n", a.Price)
}

func (a Apple) Give() {
    fmt.Printf("I want to give you %s\n", a.Name)
}

func main() {
    var p Person
    p = Apple{Name: "apples", Price: 18}

    p.Eat()
    p.Give()
}

输出:

I want to eat Apple,its price is 18
I want to give you apples

8. 空接口的定义

空接口是指没有定义任何方法的接口,它对实现不做任何要求,任何类型都实现了空接口,空接口类型的变量可以存储任意类型的变量。

type Any interface {}

var val interface {}
package main

import "fmt"

// Fruits 空接口
type Fruits interface {

}

type Apple struct {
    Name string
    Price int64
}

func main() {
    var f Fruits
    var i interface {}
    a := Apple{Name: "apples", Price: 18}

    i = "hello"
    fmt.Printf("i: %v\n", i)
    f = "你好"
    fmt.Printf("f: %v\n", f)
    f = a
    fmt.Printf("f: %v\n", f)
}

9. 空接口的应用

空接口做切片值

package main

import "fmt"

func main() {
    var fruits = make([]interface{}, 0)
    fruits = append(fruits, 1, 2)
    fmt.Println(fruits)  // 输出: [1 2]
}

空接口做map的值

package main

import "fmt"

func main() {
    var fruits = make(map[string]interface{})
    fruits["name"] = "青李"
    fruits["price"] = 10
    fmt.Println(fruits)  // 输出:map[name:青李 price:10]
}

空接口做函数的参数

package main

import "fmt"

type Apple struct {
    Name string
    Price int64
}

func main() {
    Eat(1)
    Eat(Apple{})
    Eat(Apple{Name: "青梅", Price: 8})
}

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

可做类型判断

因为空接口可以存储任意类型的值,反过来我们可以利用空接口去判断类型

package main

import "fmt"

func main() {
    var x interface{}
    x = 12345
    v, ok := x.(string)
    if ok {
        fmt.Println(v)
    } else {
        fmt.Println("类型判断失败")
    }

    x = "你好"
    v, ok = x.(string)
    if ok {
        fmt.Println(v)
    } else {
        fmt.Println("类型判断失败")
    }
}

输出:

类型判断失败
你好
Copyright © yzx该文章修订时间: 2021-11-03 20:06:05

results matching ""

    No results matching ""