GO:interface

一、感受接口

type Usb interface {
    Connect()
    Disconnect()
}
// 手机
type Phone struct {}
// 相机
type Camera struct {}
// 计算机
type Computer struct {}
// 手机实现接口所有方法
func (p Phone) Connect() {
    fmt.Println("手机连接中...")
}
func (p Phone) Disconnect() {
    fmt.Println("手机断开连接中...")
}
// 相机实现接口所有方法
func (c Camera) Connect() {
    fmt.Println("相机连接中...")
}
func (c Camera) Disconnect() {
    fmt.Println("相机断开连接中...")
}
// Working方法,接收一个Usb接口类型变量
func (c Computer) Working(usb Usb) {
    // 通过接口变量来调用Connect和Disconnect方法
    usb.Connect()
    usb.Disconnect()
}
func main() {
    // 创建结构体实例
    phone := Phone{}
    camera := Camera{}
    computer := Computer{}
    // 关键点
    computer.Working(phone)
    computer.Working(camera)
}
// 输出如下
// 手机连接中...
// 手机断开连接中...
// 相机连接中...
// 相机断开连接中...
View Code

当传入一个phone,usb就能识别是手机,传入camera,usb就能识别是相机,并别分调用其相应的方法。这很明显就是多态呀!!!

func (c Computer) Working(usb Usb) {
	usb.Connect()
	usb.Disconnect()
}
func main() {
	phone := Phone{}
	camera := Camera{}
	computer := Computer{}
	computer.Working(phone)
	computer.Working(camera)
}

二、接口介绍

interface 类型可以定义一组方法,但是这些方法不需要实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现了这个接口。

语法:

type 接口名 interface {
	method1(参数列表) 返回值列表
	method2(参数列表) 返回值列表
	...
}

三、注意事项和细节

  1. 接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的实例;
  2. 接口中所有的方法都没有方法体,即只定义方法,没有实现该方法;
  3. 一个自定义类型只有实现了某个接口,才能将该自定义类型的实例赋给该接口类型;
  4. 只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型;
  5. 一个自定义类型可以实现多个接口;
  6. golang的接口不能有任何变量;
  7. 一个接口(比如 C 接口)可以继承多个别的接口(比如 A、B 接口),这时如果要实现 C 接口,也必须将 A、B 接口中的方法也实现;注意,A、B接口中不能有相同的方法,相当于 C 接口有两个相同的方法,这是不允许的;
  8. interface 类型默认是一个指针(引用类型),如果没有对 interface 初始化就使用,那么会输出 nil ;
  9. 空接口 interface{} 没有任何方法,因此所有类型都实现了空接口,即可以把任何一个变量赋给空接口
  10. 如果一个自定义类型是使用指针方式实现一个接口的,那么需要将该自定义类型的地址赋给该接口,不然会报错。
type AInterface interface {
    Eat()
}

type Person struct {
    Name string
}

func (p Person) Eat() {
    fmt.Printf("%s正在吃饭...\n", p.Name) // 佩奇正在吃饭...
}

func main() {
    p := Person{"佩奇"}
    var a AInterface = p // 接口可以指向一个实现了该接口的自定义类型实例
    a.Eat()
}
第1、3点
type AInterface interface {
    Eat()
}

type BInterface interface {
    Sleep()
}

type Person struct {
    Name string
}

func (p Person) Eat() {
    fmt.Printf("%s正在吃饭...\n", p.Name) // 佩奇正在吃饭...
}

func (p Person) Sleep() {
    fmt.Printf("%s正在睡觉...\n", p.Name) // 佩奇正在睡觉...
}

func main() {
    p := Person{"佩奇"}
    var a AInterface = p
    var b BInterface = p
    a.Eat()
    b.Sleep()
}
第5点
type AInterface interface {
    Eat()
}

type BInterface interface {
    Sleep()
}

type CInterface interface {
    AInterface
    BInterface
    Study()
}

type Person struct {
    Name string
}

func (p Person) Eat() {
    fmt.Printf("%s正在吃饭...\n", p.Name) // 佩奇正在吃饭...
}

func (p Person) Sleep() {
    fmt.Printf("%s正在睡觉...\n", p.Name) // 佩奇正在吃饭...
}

func (p Person) Study() {
    fmt.Printf("%s正在学习...\n", p.Name) // 佩奇正在学习...
}

func main() {
    p := Person{"佩奇"}
    var c CInterface = p
    c.Eat()
    c.Sleep()
    c.Study()
}
第7点
type T interface {}

type Integer int

func main() {
    var int1 Integer
    int2 := Integer(100)
    var t T
    t = int1
    fmt.Println(t) // 0
    t = int2
    fmt.Println(t) // 100
}
第9点
type A interface {
    Eat()
}

type Person struct {
    Name string
}

// 使用 Person 指针类型实现一个接口的方法
func (p *Person) Eat() {
    fmt.Printf("%s正在吃饭...", p.Name)
}

func main() {
    p := Person{"佩奇"}
    //var a A = p // 错误!!!因为Person类型没有实现A接口,修改如下
    var a A = &p
    a.Eat()
}
第10点

四、接口最佳实践

实现对 Student 结构体切片的排序:sort.Sort(data Interface)

package main

import (
    "fmt"
    "math/rand"
    "sort"
    "time"
)

type Student struct {
    Name string
    Score int
}

type StudentSlice []Student

func (ss StudentSlice) Len() int {
    return len(ss)
}
// Less方法决定使用什么标准进行排序
// 这里按成绩从小到大排序
func (ss StudentSlice) Less(i, j int) bool {
    return ss[i].Score < ss[j].Score
}
func (ss StudentSlice) Swap(i, j int) {
    ss[i], ss[j] = ss[j], ss[i]
}

func main() {
    var ss StudentSlice
    rand.Seed(time.Now().UnixNano())
    for i := 0; i < 10; i++ {
        student := Student{
            Name: fmt.Sprintf("学生%d号", i),
            Score: rand.Intn(100),
        }
        ss = append(ss, student)
    }
    // 排序前的顺序
    for _, v := range ss {
        fmt.Println(v)
    }
    // 调用sort.Sort方法
    sort.Sort(ss)
    fmt.Println()
    // 排序后的顺序
    for _, v := range ss {
        fmt.Println(v)
    }
}
View Code

输出结果:

五、接口与继承的区别

package main

import "fmt"

type Monkey struct {
    Name string
}

type LittleMonkey struct {
    Monkey // 继承
}

type BridAble interface {
    Flying()
}
type FishAble interface {
    Swimming()
}

func (m *Monkey) Climbing() {
    fmt.Printf("%s天生就会爬树...\n", m.Name)
}

func (ls *LittleMonkey) Flying() {
    fmt.Printf("%s通过学习,会飞翔...\n", ls.Name)
}

func (ls *LittleMonkey) Swimming() {
    fmt.Printf("%s通过学习,会游泳...\n", ls.Name)
}

func main() {
    wk := LittleMonkey{Monkey{"小猴子"}}
    wk.Climbing()
    wk.Flying()
    wk.Swimming()
}
View Code

代码小结:

  1. 当 B 结构体继承了 A 结构体,那么 B 结构体就"拥有"了 A 结构体的字段和方法,并且可以直接使用;
  2. 当 B 结构体需要扩展功能,同时不希望去破坏继承关系,则实现某个接口即可。因此可以认为:实现接口可以看做是对继承的一种补充。

接口与继承总结:

①接口与继承解决的问题不同

  • 继承的价值主要在于:解决代码的复用性可维护性
  • 接口的价值主要在于:设计,设计好各种规范(方法),让其它自定义类型去实现这些方法。

②接口比继承更加灵活

  • 继承是 is - xx 的关系,而接口是 like - xx 的关系。为什么这样说? --> 我们不能说猴子是一条鱼,但可以说:猴子可以像鱼儿一样游泳。

③接口在一定程度上实现代码解耦

 

posted @ 2019-05-28 21:02  就俗人一个  阅读(319)  评论(0编辑  收藏  举报