7.1 Go interface

7.1 Go interface

雨痕-Go语言笔记

接口采用了duck type方式,在程序设计中是动态类型的一种风格
`当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。`

空接口类型interface{},类似于OOP的system.Object,可以接收任意类型。
准备交互的双方,共同遵守实现约定的规则,使得无须知道对方身份的情况下进行协作。
接口要实现的是做什么,而不关心怎么做,谁来做。

可以先实现类型,而后再抽象出所需接口。称作非侵入式设计。

概念

Go语言的主要设计者之一罗布·派克(Rob Pike)曾经说过,如果只能选择一个Go语言的特 性移植到其他语言中,他会选择接口

接口在Go语言有着至关重要的地位。如果说goroutinechannel是支撑起Go语言的并发模型 的基石,让Go语言在如今集群化多核化的时代成为一道极为亮丽的风景,那么接口是Go语言 整个类型系统的基石,让Go语言在基础编程哲学的探索上达到前所未有的高度。

Go语言在编程哲学上是变革派,而不是改良派。这不是因为Go语言有goroutine和channel, 而更重要的是因为Go语言的类型系统,更是因为Go语言的接口。Go语言的编程哲学因为有接口 而趋近完美。

接口只有方法声明,没有实现,也没有数据字段。
接口可以匿名嵌入到其他接口。
对象赋值给接口时,会发生拷贝。
只有当接口存储的类型和对象都是nil时,接口等于nil。
空接口可以接收任意的数据类型。
一个类型可以实现多个接口。
接口变量名习惯以 er 结尾。

接口类型是对其它类型行为的抽象和概括,接口类型不会和特定的实现细节绑定。
Go接口独特在它是隐式实现的,这是指:一个结构体只要实现了接口要求的所有方法,我们就说这个结构体实现了该接口。

接口在现实世界也是有真实场景的,如同笔记本上都有USB插口,且不用担心这个插槽是为手机U盘平板哪一个准备的,因为笔记本的usb插槽和各种设备的厂家统一了USB的插槽规范。


1.2. 接口语法

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

interface类型可以定义一组方法,且不需要实现这些方法!并且interface不能有任何变量。
只要有一个变量类型,含有接口中的所有方法,就说这个变量类型实现了这个接口。

Go多态与接口

package main

import "fmt"

//定义一个Usb接口,且定义Usb功能方法
type Usb interface {
    Start()
    Stop()
}

type Phone struct {
    Name  string
    Price int
}

//让手机Phone实现Usb接口的方法
func (p Phone) Start() {
    fmt.Println("手机已连接USB,开始工作")
}

//必须实现接口所有的方法,少一个都报错  如下:Phone does not implement Usb (missing Stop method)
func (p Phone) Stop() {
    fmt.Println("手机断开了USB,停止工作")
}

type IPad struct {
    Name  string
    Price int
}

func (p IPad) Start() {
    fmt.Println("ipad已经连接USB,开始工作")
}

func (p IPad) Stop() {
    fmt.Println("ipad断开了USB,停止工作")
}

//定义一个电脑结构体,这个结构体可以实现usb的接口
type Computer struct {
}

//定义working方法,接收Usb接口类型变量
//实现Usb接口声明的所有方法
//这是一个多态的函数,调用同一个Working函数,不同的执行结果
func (mbp Computer) Working(usb Usb) {
    //不同的usb实参,实现不同的功能
    //只要实现了usb接口的数据类型,那么这个类型的变量,就可以给usb接口变量赋值
    usb.Start()
    usb.Stop()
}

func main() {
    //分别创建结构体对象
    c := Computer{}
    p := Phone{"苹果手机", 6999}
    i := IPad{"华为平板", 7999} 

    //
    //手机连接笔记本,插上手机
    c.Working(p)
    fmt.Printf("名字:%v 价格:%d
", p.Name, p.Price)

    fmt.Println("------------------")
    //平板连接笔记本,插上平板
    c.Working(i)
    fmt.Printf("名字:%v 价格:%d
", i.Name, i.Price)
}

接口实例2

package main

import "fmt"

//员工接口
type Employer interface {
    CalcSalary() float32
}

//开发者
type Programer struct {
    name  string
    base  float32
    extra float32
}

//创建开发者实例
func NewProgramer(name string, base float32, extra float32) Programer {
    return Programer{
        name,
        base,
        extra,
    }
}

//计算开发者工资,实现了CalcSalary方法
func (p Programer) CalcSalary() float32 {
    return p.base
}

//销售群体
type Sale struct {
    name  string
    base  float32
    extra float32
}

//创建销售实例
func NewSale(name string, base float32, extra float32) Sale {
    return Sale{
        name,
        base,
        extra,
    }
}

//实现了CalcSalary方法
func (p Sale) CalcSalary() float32 {
    return p.base + p.extra*p.base*0.5
}

//计算所有人的工资接收参数,接口切片
func calcAll(e []Employer) float32 {
    /*
        fmt.Println(e)
        [{码云 50000 0} {刘抢东 40000 0} {麻花藤 30000 0} {格格 3000 2.5} {小雪 1800 2.5} {小雨 2000 2.5}]
    */
    var cost float32
    //忽略索引,v是每一个结构体
    for _, v := range e {
        cost = cost + v.CalcSalary()
    }
    return cost
}
func main() {
    p1 := NewProgramer("码云", 50000.0, 0)
    p2 := NewProgramer("刘抢东", 40000, 0)
    p3 := NewProgramer("麻花藤", 30000, 0)

    s1 := NewSale("格格", 3000, 2.5)
    s2 := NewSale("小雪", 1800, 2.5)
    s3 := NewSale("小雨", 2000, 2.5)

    var employList []Employer
    employList = append(employList, p1)
    employList = append(employList, p2)
    employList = append(employList, p3)
    employList = append(employList, s1)
    employList = append(employList, s2)
    employList = append(employList, s3)

    cost := calcAll(employList)
    fmt.Printf("这个月人力成本:%f
", cost)
}

1.3. Go接口细节

1.接口本身不能创建实例,但是可以指向一个实现了该接口的变量实例,如结构体
2.接口中所有方法都没有方法体,是没有实现的方法
3.Go中不仅是struct可以实现接口,自定义类型也可以实现接口,如type myInt int 自定义类型
4.一个自定义类型,只有实现了某个接口,才可以将自定义类型的实例变量,赋给接口类型,否则报错missing xx method
5.一个自定义类型,可以实现多个接口(实现多个接口的所有方法)
6.接口类型不得写入任何变量 如
type Usb interface{
    method1()
    method2()
    Name string  //错误,编译器不通过
}
7.接口A可以继承多个别的接口B、接口C,想要实现A,也必须实现B、C所有方法,称作接口组合
8.interface类型,默认是指针(引用类型),如果没初始直接使用,输出nil,可以赋给实现了接口的变量
9.空接口interface{},没有任何类型,也就是实现了任何类型,可以吧任何一个变量赋给空接口
10.匿名组合的接口,不可以有同名方法,否则报错duplicate method

1.3.1. 空接口

package main

import "fmt"

type A interface {
}

type Cat struct {
    name string
    age  int
}

type Person struct {
    name string
    sex  string
}

//参数类型是接口类型,可以接收任意类型数据
func test1(a A) {
    fmt.Println(a)

}

//
func test2(a interface{}) {
    fmt.Println(a)

}

//数组元素是接口,可以存放任意不同的数据类型,输出不同类型
func test3(slice2 []interface{}) {

    for i := 0; i < len(slice2); i++ {
        //数组中元素类型不同,通过接口的类型断言判断
        switch ins := slice2[i].(type) {
        case Cat:
            fmt.Println("	传入cat对象:", ins.name, ins.age)
        case Person:
            fmt.Println("	传入person对象:", ins.name, ins.sex)
        case int:
            fmt.Println("	传入了int类型:", ins)
        case string:
            fmt.Print("	传入了string类型:", ins)
        }
    }
}

func main() {
    a1:=Cat{"花花",1}
    p1:=Person{"锐萌萌","女性"}
    i1:=123
    s1:="峡谷之巅,最强王者"

    test1(a1)
    test2(p1)
    //test3接收切片类型
    //初始化一个切片s1
    slice1:=[]interface{}{}
    fmt.Println(slice1,len(slice1),cap(slice1))
    fmt.Println("你好")

    slice1=append(slice1,a1,p1,i1,s1)
    fmt.Println(slice1)

    //传入切片
    test3(slice1)
}
原文地址:https://www.cnblogs.com/open-yang/p/11256892.html