golang 接口inferface和多态

1.接口快速入门

基本介绍:

inferface类型可以定义一组方法,但是这些不需要实现。并且interface不能包含任何变量。到某个自定义类型(比如结构体Phone)要使用的时候,在根据具体情况把这些方法写出来。interface是引用类型。引用类型包含:指针,slice切片,map,管道chan,interface都是引用类型。

基本语法:

type 接口名 interface{                        func {t 自定义类型} method1{参数列表} 返回值列表{

  method1(参数列表) 返回值列表       实现接口的所以方法             //方法实现

  method2(参数列表) 返回值列表   -------------->       }   

  ...                               func {t 自定义类型} method2{参数列表} 返回值列表{

}                                    //方法实现

                                  }

                                  //...

小结说明:

1)接口里所有方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的多态和高内聚低耦合的思想

2)Golang中的接口,不需要显式的实现,只要一个变量,含有接口类型的所有方法,那么这个变量就实现这个接口。因此,Golang中没有implement这样的关键字。

复制代码
package main

import "fmt"

type Usb interface {
    //声明了两个没有实现的方法
    Start()
    Stop()
}

//手机
type Phone struct {
}

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

//相机
type Camera struct {
}

//让Camera实现Usb接口的方法
func (c Camera) Start() {
    fmt.Println("相机开始工作")
}
func (c Camera) Stop() {
    fmt.Println("相机停止工作")
}

//计算机
type Computer struct {
}

//计算机方法Working,接收Usb接口类型变量
//只要实现了Usb接口,所谓实现Usb接口, 就是指实现了Usb接口声明的所有方法(Phone Camera)
func (C Computer) Working(usb Usb) {
    //通过usb接口变量来调用Start和Stop方法
    usb.Start()
    usb.Stop()
}
func main() {
    computer := Computer{}
    phone := Phone{}
    camera := Camera{}

    //关键点
    computer.Working(phone)
    computer.Working(camera)
}
复制代码

2.注意事项和细节:

1)接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)。

2)接口中所有的方法都没有方法体,即都是没有实现的方法。

3)在Golang中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口。

4)一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋值给接口类型。

5)只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型。

复制代码
package main

import "fmt"

type AInterface interface {
    Say()
}
type Student struct {
}

func (stu Student) Say() {
    fmt.Println("stu say()")
}

type integer int

func (i integer) Say() {
    fmt.Println("integer Say i =", i)
}
func main() {
    stu := Student{}
    var a AInterface = stu
    a.Say()

    var i integer = 10
    var b AInterface = i
    b.Say()
}
复制代码

6)一个自定义类型可以实现多个接口

复制代码
package main

import "fmt"

type AInterface interface {
    Say()
}
type BInterface interface {
    Hello()
}
type Student struct {
}

func (stu Student) Say() {
    fmt.Println("stu say()")
}
func (stu Student) Hello() {
    fmt.Println("stu Hello()")
}
func main() {
    stu := Student{}
    var a AInterface = stu
    var b BInterface = stu
    a.Say()
    b.Hello()
}
复制代码

7)Golang接口中不能有任何变量

8)一个接口(比如A接口)可以继承多个别的接口(比如B,C接口),这时如果要实现A接口,也必须将B,C接口的方法也全部实现。

复制代码
package main

import "fmt"

type Binterface interface {
    test01()
}
type Cinterface interface {
    test02()
}
type Ainterface interface {
    Binterface
    Cinterface
    test03()
}

type Student struct {
}

func (stu Student) test01() {
    fmt.Println("test01()")
}
func (stu Student) test02() {
    fmt.Println("test02()")
}
func (stu Student) test03() {
    fmt.Println("test03()")
}

func main() {
    var stu Student
    var a Ainterface = stu
    a.test01()
}
复制代码

9)interface类型默认是一个指针(引用类型),如果没有对interface初始化就使用,那么会输出nil

10)空接口interface{}没有任何方法,所以类型都实现了空接口,可以把任何一个变量都赋给空接口类型。

11)一个接口中有两个同名的方法会报错。如A接口中有B,C接口,且B,C接口中都有test01(),这样就会报错。

3.接口实例

实现对Hero结构体切片的排序:sort.Sort(data interface)
复制代码
package main

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

//实现对Hero结构体切片的排序:sort.Sort(data interface)
//1.声明Hero结构体
type Hero struct {
    Name string
    Age  int
}

//2.声明Hero结构体切片类型
type HeroSlice []Hero

//实现接口的三个方法:Len(),Less(i int, j int),Swap(i int, j int)
func (hs HeroSlice) Len() int {
    return len(hs)
}

func (hs HeroSlice) Less(i int, j int) bool {
    return hs[i].Age < hs[j].Age
}

func (hs HeroSlice) Swap(i int, j int) {
    hs[i], hs[j] = hs[j], hs[i]
}

func main() {
    //定义切片
    hs := HeroSlice{}
    //赋值切片
    for i := 0; i < 10; i++ {
        hero := Hero{}
        hero.Name = fmt.Sprintf("hero~%d", rand.Intn(100))
        hero.Age = rand.Intn(100)
        hs = append(hs, hero)
    }
    //排序
    sort.Sort(hs)
    for _, v := range hs {
        fmt.Println(v)
    }
}
复制代码

4.接口和继承

小结

1)当A结构体继承了B结构体,那么A结构体就自动继承了B结构体的字段和方法,并且可以直接使用

2)当A结构体需要扩展功能,同事不希望去破坏继承关系,则可以去实现某个接口即可,因此我们可以认为:实现接口是对继承机制的补充。

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

4)接口比继承更加灵活:继承是满足is -a的关系,而接口只需要满足like-a的关系。

5)接口在一定程度上实现代码解耦。

复制代码
package main

import "fmt"

type Monkey struct {
    Name string
}

//LittleMonkey继承Monkey
type LittleMonkey struct {
    Monkey
}

func (mo *Monkey) Climbing() {
    fmt.Printf("%v   会爬树\n", mo.Name)
}

//定义接口BirdAble,FishAble来扩展结构体的方法
type BirdAble interface {
    Flying()
}
type FishAble interface {
    Swapping()
}

func (limo *LittleMonkey) Flying() {
    fmt.Printf("%v   会飞\n", limo.Name)
}
func (limo *LittleMonkey) Swapping() {
    fmt.Printf("%v   会游泳\n", limo.Name)
}
func main() {
    limo := LittleMonkey{}
    limo.Name = "悟空"
    limo.Climbing()
    limo.Flying()
    limo.Swapping()
}
复制代码

5.多态

变量(实例)具有多种形态。面向对象的第三大特征,在Go语言,多态特征是通过接口实现的。可以按照统一的接口来调用不同的实现。这时接口变量就呈现出不同的形态。

多态参数:

Usb既可以接收手机变量,又可以接收相机变量,就体现了Usb接口的多态性。

多态数组:

给Usb数组中,存放Phone结构体和 Camera结构体变量。因为有了多态数组才可以存放不同类型是元素,普通数组只能存放一种类型的元素。

复制代码
package main

import "fmt"

type Usb interface {
    //声明了两个没有实现的方法
    Start()
    Stop()
}

//手机
type Phone struct {
    Name string
}

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

//相机
type Camera struct {
    Name string
}

//让Camera实现Usb接口的方法
func (c Camera) Start() {
    fmt.Println("相机开始工作")
}
func (c Camera) Stop() {
    fmt.Println("相机停止工作")
}

func main() {
    //定义一个Usb接口数组,可以存放Phone和Camera的结构体变量
    var usbArr [3]Usb
    usbArr[0] = Phone{"手机1"}
    usbArr[1] = Phone{"手机2"}
    usbArr[2] = Camera{"相机"}
    fmt.Println(usbArr)
}
复制代码

6.类型断言

基本概念:

类型断言,由于接口是一般类型,不知道具体类型,如果要转成具体的类型,就需要使用类型断言。

    var x interface{}
    var b float32 = 1.1 
    x = b //空接口,可以接收任意类型
    //x=>float32【使用类型断言】
    y := x.(float32)
    fmt.Printf("y的类型是%T 值是=%v", y, y)

小结:

在进行类型断言时,如果类型不匹配,就会报panic,因此进行类型断言时,要确保原来的空接口指向的就是断言类型。

如何在进行断言时,带上检测机制,如果成功就ok,否则也不要报panic

复制代码
    var x interface{}
    var b float32 = 1.1
    x = b //空接口,可以接收任意类型
    //x=>float32【使用类型断言】
    y, ok := x.(float64)
    if ok {
        fmt.Println("convert success")
        fmt.Printf("y的类型是%T 值是=%v", y, y)
    } else {
        fmt.Println("convert fail")
    }
    fmt.Println("代码继续执行")
复制代码

实例1:

对前面的Usb接口做改进:

给Phone结构体增加一个特有的方法call(),当Usb接口接收的是Phone变量时,还需要调用call方法。

复制代码
package main

import (
    "fmt"
)

type Usb interface {
    //声明了两个没有实现的方法
    Start()
    Stop()
}

//手机
type Phone struct {
    Name string
}

//让Phone实现Usb接口的方法
func (p Phone) Start() {
    fmt.Println("手机开始工作")
}
func (p Phone) Stop() {
    fmt.Println("手机停止工作")
}
func (p Phone) Call() {
    fmt.Println("手机在打电话")
}

//相机
type Camera struct {
    Name string
}

//让Camera实现Usb接口的方法
func (c Camera) Start() {
    fmt.Println("相机开始工作")
}
func (c Camera) Stop() {
    fmt.Println("相机停止工作")
}

//计算机
type Computer struct {
}

//计算机方法Working,接收Usb接口类型变量
//只要实现了Usb接口,所谓实现Usb接口, 就是指实现了Usb接口声明的所有方法(Phone Camera)
func (C Computer) Working(usb Usb) {
    //通过usb接口变量来调用Start和Stop方法
    usb.Start()
    //如果usb是指向Phone结构体变量,则还需要使用Call方法
    //使用类型断言[注意使用的好处]
    if phone, ok := usb.(Phone); ok {
        phone.Call()
    }
    usb.Stop()
}
func main() {
    //定义一个Usb接口数组,可以存放Phone和Camera的结构体变量
    var usbArr [3]Usb
    usbArr[0] = Phone{"手机1"}
    usbArr[1] = Phone{"手机2"}
    usbArr[2] = Camera{"相机"}
    var computer Computer
    for _, v := range usbArr {
        computer.Working(v)
    }
}
复制代码

实例2:

写一个函数,循环判断传入参数的类型

复制代码
type Student struct {
}

func TypeJudge(item ...interface{}) {
    for index, x := range item {
        switch x.(type) {
        case bool:
            fmt.Printf("第%v个参数是bool类型,值是%v\n", index, x)
        case float32:
            fmt.Printf("第%v个参数是float32类型,值是%v\n", index, x)
        case float64:
            fmt.Printf("第%v个参数是float64类型,值是%v\n", index, x)
        case int, int32, int64:
            fmt.Printf("第%v个参数是整数类型,值是%v\n", index, x)
        case string:
            fmt.Printf("第%v个参数是string类型,值是%v\n", index, x)
        case Student:
            fmt.Printf("第%v个参数是Student类型,值是%v\n", index, x)
        case *Student:
            fmt.Printf("第%v个参数是*Student类型,值是%v\n", index, x)
        default:
            fmt.Printf("第%v个参数的类型不确定,值是%v\n", index, x)
        }

    }
}
func main() {
    var n1 float32 = 1.1
    var n2 float64 = 1.2
    var n3 int32 = 30
    var name string = "tom"
    n4 := 300
    address := "北京"
    stu := Student{}
    stu2 := &Student{}
    TypeJudge(n1, n2, n3, name, n4, address, stu, stu2)
}
复制代码

 

posted @   潇潇暮鱼鱼  阅读(97)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示