• Go 接口和多态


    在讲解具体的接口之前,先看如下问题。

    使用面向对象的方式,设计一个加减的计算器

    代码如下:

    package main
    
    import "fmt"
    
    //父类,这是结构体
    type Operate struct {
        num1 int
        num2 int
    }
    
    //加法子类,这是结构体
    
    type Add struct {
        Operate
    }
    
    //减法子类,这是结构体
    
    type Sub struct {
        Operate
    }
    
    //加法子类的方法
    func (a *Add) Result() int {
        return a.num1 + a.num2
    }
    可以看到ADD里面是用父类结构体的,然后直接返回num1+num2就行了
    //减法子类的方法
    func (s *Sub) Result() int {
        return s.num1 - s.num2
    }
    可以看到Sub里面是用父类结构体的,然后直接返回num1-num2就行了
    //方法调用
    func main0201() {
        //创建加法对象
        //var a Add
        //a.num1 = 10
        //a.num2 = 20
        //v := a.Result()
        //fmt.Println(v)
    //可以看到调用起来还是很简单的,直接给父类结构体的属性赋值,然后调用加法的方法就行。
        //创建减法对象
        var s Sub
        s.num1 = 10
        s.num2 = 20
        v := s.Result()
        fmt.Println(v)
    }
    //可以看到调用起来还是很简单的,直接给父类结构体的属性赋值,然后调用减法的方法就行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    以上实现非常简单,但是有个问题,在main()函数中,当我们想使用减法操作时,创建减法类的对象,调用其对应的减法的方法。但是,有一天,系统需求发生了变化,要求使用加法,不再使用减法,那么需要对main()函数中的代码,做大量的修改。将原有的代码注释掉,创建加法的类对象,调用其对应的加法的方法。有没有一种方法,让main()函数,只修改很少的代码就可以解决该问题呢?有,要用到接下来给大家讲解的接口的知识点。

    一、 什么是接口

    接口就是一种规范与标准,在生活中经常见接口,例如:笔记本电脑的USB接口,可以将任何厂商生产的鼠标与键盘,与电脑进行链接。为什么呢?原因就是,USB接口将规范和标准制定好后,各个生产厂商可以按照该标准生产鼠标和键盘就可以了。

    在程序开发中,接口只是规定了要做哪些事情,干什么。具体怎么做,接口是不管的。这和生活中接口的案例也很相似,例如:USB接口,只是规定了标准,但是不关心具体鼠标与键盘是怎样按照标准生产的.

    在企业开发中,如果一个项目比较庞大,那么就需要一个能理清所有业务的架构师来定义一些主要的接口,这些接口告诉开发人员你需要实现那些功能。

    二、 接口定义

    接口定义的语法如下:

    //先定义接口 一般以er结尾 根据接口实现功能
    type Humaner interface {
      //方法 方法的声明
      sayhi()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    怎样具体实现接口中定义的方法呢?

    //Student的结构体
    type student11 struct {
        name  string
        age   int
        score int
    }
    //Student的打印方法
    func (s *student11)sayhi()  {
        fmt.Printf("大家好,我是%s,今年%d岁,我的成绩%d分\n",s.name,s.age,s.score)
    }
    //teacher11的结构体
    type teacher11 struct {
        name    string
        age     int
        subject string
    }
    //teacher11的方法
    func (t *teacher11)sayhi()  {
        fmt.Printf("大家好,我是%s,今年%d岁,我的学科是%s\n",t.name,t.age,t.subject)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    具体的调用如下:

    func main() {
        //接口是一种数据类型 可以接收满足对象的信息
        //接口是虚的  方法是实的
        //接口定义规则  方法实现规则
        //接口定义的规则  在方法中必须有定义的实现
        var h Humaner
    
        stu := student11{"小明",18,98}
        //stu.sayhi()
        //将对象信息赋值给接口类型变量
        h = &stu
        h.sayhi()
    //直接将Student的对象赋值给了h接口,然后就能实现方法的调用
        tea := teacher11{"老王",28,"物理"}
        //tea.sayhi()
        //将对象赋值给接口 必须满足接口中的方法的声明格式
        h = &tea
        h.sayhi()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    只要类(结构体)实现对应的接口,那么根据该类创建的对象,可以赋值给对应的接口类型。

    接口的命名习惯以er结尾。

    三、 多态

    接口有什么好处呢?实现多态。

    多态就是同一个接口,使用不同的实例而执行不同操作

    所谓多态指的是多种表现形式,如下图所示:
    在这里插入图片描述

    使用接口实现多态的方式如下:

    package main
    
    import "fmt"
    
    //先定义接口  一般以er结尾  根据接口实现功能
    type Humaner1 interface {
        //方法  方法的声明
        sayhi()
    
    }
    
    //student12的结构体
    type student12 struct {
        name  string
        age   int
        score int
    }
    //student12的方法
    func (s *student12)sayhi()  {
        fmt.Printf("大家好,我是%s,今年%d岁,我的成绩%d分\n",s.name,s.age,s.score)
    }
    //teacher12的结构体
    type teacher12 struct {
        name    string
        age     int
        subject string
    }
    //teacher12的方法
    func (t *teacher12)sayhi()  {
        fmt.Printf("大家好,我是%s,今年%d岁,我的学科是%s\n",t.name,t.age,t.subject)
    }
    
    //多态的实现
    //将接口作为函数参数  实现多态
    func sayhello(h Humaner1)  {
        h.sayhi()
    }
    
    func main() {
    
        stu := student12{"小明",18,98}
        //调用多态函数
        sayhello(&stu)
    
        tea := teacher12{"老王",28,"Go"}
        sayhello(&tea)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    关于接口的定义,以及使用接口实现多态,但是多态有什么好处呢?现在还是以开始提出的计算器案例给大家讲解一下。

    四、多态案例

    使用多态的功能,实现一个加减计算器。完整代码如下:

    package main
    
    import "fmt"
    
    // 定义接口
    type Opter interface {
    	// 方法声明
    	Result() int
    }
    
    // 父类
    type Operate struct {
    	num1 int
    	num2 int
    }
    
    // 加法子类
    type Add struct {
    	Operate
    }
    
    // 加法子类的方法
    func (a *Add) Result() int {
    	return a.num1 + a.num2
    }
    
    // 减法子类
    type Sub struct {
    	Operate
    }
    
    // 减法子类的方法
    func (s *Sub) Result() int {
    	return s.num1 - s.num2
    }
    
    // 多态实现
    func Result(o Opter) {
    	v := o.Result()
    	fmt.Println(v)
    }
    
    // 上面是定义的方法
    func main() {
    	//创建加法对象
    	//var a Add
    	//a.num1 = 10
    	//a.num2 = 20
    	//v := a.Result()
    	//fmt.Println(v)
    
    	//2.通过接口实现
    	//var o Opter
    	//var a Add = Add{Operate{10, 20}}
    	//o = &a
    	//value := o.Result()
    	//fmt.Println(value)
    
    	//3.多态实现
    	var a Add = Add{Operate{10, 20}}
    	Result(&a)
    
    	var s Sub = Sub{Operate{10, 20}}
    	Result(&s)
    }
    
    
    30
    -10
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    四、 接口继承与转换

    接口也可以实现继承:

    // 先定义接口  一般以er结尾  根据接口实现功能
    type Humaner2 interface { //子集
    	//方法  方法的声明
    	sayhi()
    }
    
    type Personer interface { //超集
    	Humaner2 //继承sayhi()
    
    	sing(string)
    }
    
    type student13 struct {
    	name  string
    	age   int
    	score int
    }
    
    func (s *student13) sayhi() {
    	fmt.Printf("大家好,我是%s,今年%d岁,我的成绩%d分\n", s.name, s.age, s.score)
    }
    
    func (s *student13) sing(name string) {
    	fmt.Println("我为大家唱首歌", name)
    }
    
    func main() {
    	//接口类型变量定义
    	var h Humaner2
    	var stu student13 = student13{"小吴", 18, 59}
    	h = &stu
    	h.sayhi()
    
    	//接口类型变量定义
    	var p Personer
    	p = &stu
    	p.sayhi()
    	p.sing("大碗面")
    }
    
    
    大家好,我是小吴,今年18岁,我的成绩59分
    大家好,我是小吴,今年18岁,我的成绩59
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    接口继承后,可以实现“超集”接口转换“子集”接口,代码如下:

    package main
    
    import "fmt"
    
    // 先定义接口  一般以er结尾  根据接口实现功能
    type Humaner2 interface { //子集
    	//方法  方法的声明
    	sayhi()
    }
    
    type Personer interface { //超集
    	Humaner2 //继承sayhi()
    
    	sing(string)
    }
    
    type student13 struct {
    	name  string
    	age   int
    	score int
    }
    
    func (s *student13) sayhi() {
    	fmt.Printf("大家好,我是%s,今年%d岁,我的成绩%d分\n", s.name, s.age, s.score)
    }
    
    func (s *student13) sing(name string) {
    	fmt.Println("我为大家唱首歌", name)
    }
    
    func main() {
    	//接口类型变量定义
    	var h Humaner2 //子集
    	var p Personer //超集
    	var stu student13 = student13{"小吴", 18, 59}
    
    	p = &stu
    	//将一个接口赋值给另一个接口
    	//超集中包含所有子集的方法
    	h = p //ok
    
    	h.sayhi()
    
    	//子集不包含超集
    	//不能将子集赋值给超集
    	//p = h  //err
    	//p.sayhi()
    	//p.sing("大碗面")
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    五、 空接口

    空接口(interface{})不包含任何的方法,正因为如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值。例如:

    func test1() {
    	fmt.Println("test")
    }
    
    func main() {
    	// 空接口类型的切片
    	var i []interface{}
    	fmt.Printf("%T\n", i)
    	i = append(i, 1, 3.14, "aaa", test1)
    	fmt.Println(i)
    
    	for idx := 0; idx < len(i); idx++ {
    		fmt.Println(i[idx])
    	}
    }
    
    
    []interface {}
    [1 3.14 aaa 0xd5420]
    1                   
    3.14                
    aaa                 
    0xd5420   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
  • 相关阅读:
    支持在线写SQL的Oracle学习免费网站(个人常使用)
    std::apply 源码分析
    【计算机视觉40例】案例27:人脸检测
    Nocas为什么会在SpringBoot启动完就会注册呢?
    Protocol - Exploits学习笔记
    涨知识,关于代码签名证书10大常见问题解答
    MyBatis插件原理探究和自定义插件实现
    当当网按关键字搜索dangdang商品 API
    数据结构之排序
    ffplay源码解析-main入口函数
  • 原文地址:https://blog.csdn.net/weixin_47906106/article/details/132740727