当前位置: 首页 > news >正文

多态与接口(Golang)

多态实现的基本要求

  1. 有一个父类(有接口)
  2. 有子类实现了父类的全部方法
  3. 父类类型的变量(指针)指向(引用)子类的具体实现数据变量

案例

案例1:父类实现的是子类的共性

type Animal interface {Yell() //叫声Eat()SizePrint()
}//对于一个接口而言 没有所谓的继承 而是实现了这个接口所有方法的类 就可以使用这个接口// 下面 我创建一个案例 来实现三种动物的叫声 进食和体型
type Dog struct {Color stringSize  stringSound stringFood  string
}
type Cat struct {Food  stringSize  stringSound stringLike  string
}
type Pig struct {Food    stringSize    stringSound   stringAbility string
}// 接下来 我们来实现猫的方法
func (c Cat) Eat() {fmt.Println("猫猫爱吃", c.Food)
}
func (c Cat) SizePrint() {fmt.Println("猫猫的体型是", c.Size)
}
func (c Cat) Yell() {fmt.Println(c.Sound)
}// 接下来 我们来实现狗的方法
func (d *Dog) Yell() {println(d.Sound)
}
func (d *Dog) Eat() {println("狗狗吃", d.Food)
}
func (d *Dog) SizePrint() {fmt.Println("狗狗的体型是", d.Size)
}// 接下来 我们来实现猪的方法
func (p *Pig) Yell() {println(p.Sound)
}
func (p *Pig) Eat() {println("猪猪吃", p.Food)
}
func (p *Pig) SizePrint() {fmt.Println("狗狗的体型是", p.Size)
}func test1() {dog1 := Dog{Color: "red",Size:  "big",Sound: "wangwang",Food:  "bone",} //这里 我们创建的是一个狗的实例 现在我们尝试使用接口类去调用方法var animal Animalanimal = &dog1 //我们可以讲这个借口指针指向dog1这个实例animal.Yell()animal.Eat()animal.SizePrint()animal = &Cat{Food:  "fish",Size:  "small",Sound: "miao",Like:  "mouse",}animal.Yell()animal.Eat()animal.SizePrint()//通过上面的两个案例,我们可以看出,接口指向一个已有实例或者指向一个创建的实例}
  1. 首先,我们注意到,对于一个类的方法而言,我们不需要额外传入这个类的参数,而这个方法直接就可以使用这个类的参数。这点在下面一个案例的多态接口方法中也是如此。
  2. 再者,我们有可以发现,正如上面所说,一个类想要使用某个接口,就需要实现该接口的所有方法
  3. 再者,一个接口(父类)可以通过接收变量和直接赋值两种方式接收子类的地址(更清晰的理解是指针指向子类)

输出结果:

wangwang
狗狗吃 bone       
狗狗的体型是 big  
miao              
猫猫爱吃 fish     
猫猫的体型是 small

案例2:通过接口实现一个多态方法


// 下面 我们实现一下多态的案例
func ShowAnimal(animal Animal) {animal.Yell()animal.Eat()//animal.SizePrint()//fmt.Println("输入的动物体型为", animal.Size) 需要注意的是 这个接口不能直接调用输出类的元素 只能调用方法
}func test2() {dog2 := Dog{Size: "small", Food: "bone", Sound: "wangwang"}cat2 := Cat{Size: "big", Food: "fish", Sound: "miao", Like: "mouse"}//这就实现了一个多态的方法 即同一个方法可以被不同的类调用 (只要这些类属于一个接口)ShowAnimal(&dog2)ShowAnimal(&cat2)
}
  1. ShowAnimal这个方法就是一个父类的方法,我们将子类的地址传进去,就可以实现统一个方法被不同类调用的效果

  2. 值得注意的是ShowAnimal里的两行注释,说明了接口这个可以调用方法,而不能去直接引用这个子类的成员变量。而为了解决这个问题,我们通常使用的方法是构造一个“返回值函数”,案例如下

    // 下面 我们实现一下多态的案例
    type Animal2 interface {Yell()Eat()SizePrint()GetSize() string//解决方案
    }func (d Dog) GetSize() string {return d.Size
    }
    func (c Cat) GetSize() string {return c.Size
    }
    func (p Pig) GetSize() string {return p.Size
    }func ShowAnimal(animal Animal2) {animal.Yell()animal.Eat()//animal.SizePrint()fmt.Println("输入的动物体型为", animal.GetSize())
    }func test2() {dog2 := Dog{Size: "small", Food: "bone", Sound: "wangwang"}cat2 := Cat{Size: "big", Food: "fish", Sound: "miao", Like: "mouse"}//这就实现了一个多态的方法 即同一个方法可以被不同的类调用 (只要这些类属于一个接口)ShowAnimal(&dog2)ShowAnimal(&cat2)
    }
    

    运行结果:

    wangwang
    狗狗吃 bone           
    输入的动物体型为 small
    miao                  
    猫猫爱吃 fish         
    输入的动物体型为 big  
    

运行结果:

wangwang
狗狗吃 bone  
miao         
猫猫爱吃 fish

案例3:一个类可以有多个接口

这和函数的原则是很类似的:每个接口(函数)实现的功能很少,这样可以使这个接口(函数)的使用率提高和适用场景增多。

//接下来 我想说明一点 就是一个类可以属于多个接口 我们拿智能手机来举例子
// 在智能手机中 我们可以实现“电话的功能”(接听 拨打 发信息) 也可以实现“相机的功能”(拍照 录像) 还可以实现视频播放器的功能(看电视剧)type Phone interface {//这个接口有智能手机和移动电话两个类Listen()Call()Message()
}type Camera interface {//这个接口有智能手机和相机两个类TakePhoto()RecordVideo()
}type VideoPlayer interface {//这个接口有智能手机和电视两个类PlayVideo()
}type MobilePhone struct {Sound stringTel   stringText  stringName  string
}
type camera struct {Take   stringRecord stringName   string
}
type TV struct {Play stringName string
}type Smartphone struct {MobilePhonecameraTVName string
}// 下面三个实现的是一个手机类接口的方法
func (m MobilePhone) Listen() {println(m.Name, "发出", m.Sound, "的声响")
}
func (m MobilePhone) Call() {println(m.Name, m.Tel, "可以拨打电话")
}
func (m MobilePhone) Message() {println(m.Name, "可以发送信息,内容为", m.Text)
}// 下面三个实现的是一个相机类接口的方法
func (c camera) TakePhoto() {println(c.Name, "可以", c.Take)
}
func (c camera) RecordVideo() {println(c.Name, "可以", c.Record)
}// 下面三个实现的是一个电视类接口的方法
func (t TV) PlayVideo() {println(t.Name, "可以", t.Play)
}// 下面是三个多态函数
func ShowPhone(phone Phone) {phone.Listen()phone.Call()phone.Message()
} //由于传入参数的限制 这个函数可以被所有手机类调用
func ShowCamera(camera Camera) {camera.TakePhoto()camera.RecordVideo()
}
func ShowTV(TV VideoPlayer) {TV.PlayVideo()
}func test3() {smartphone := Smartphone{MobilePhone: MobilePhone{Sound: "beep", Tel: "123456", Text: "hello", Name: "智能手机"},camera:      camera{Take: "take", Record: "record", Name: "智能手机"},TV:          TV{Play: "play", Name: "智能手机"},//Name:        "智能手机",} //创建一个智能手机实例ShowPhone(&smartphone)ShowCamera(&smartphone)ShowTV(&smartphone)
}
  1. 我们可以发现,只要是父类实现了某个方法,那么子类就可以使用该方法而无需自己实现,但问题就是该方法调用的成员变量只能是子类嵌套里面的(就像在实例里面,我在每个父类中都创建了Name:“智能手机”这个东西)。这个只能说仁者见仁智者见智了,如果是省事减少新类型方法的创建,就直接继承即可,在结构体实例里面略显别扭的写入成员变量;或者可以自己新创建一个方法(需要配套称整个接口),来实现:

    // 智能手机类方案
    type SmartPhone interface {Listen()Call()Message()TakePhoto()RecordVideo()PlayVideo()
    }func (smartphone Smartphone) Listen() {println(smartphone.Name, "发出", smartphone.Sound, "的声响")
    }
    func (smartphone Smartphone) Call() {println(smartphone.Name, smartphone.Tel, "可以拨打电话")
    }// 下面是三个多态函数
    func ShowPhone(phone Phone) {phone.Listen()phone.Call()phone.Message()
    } //由于传入参数的限制 这个函数可以被所有手机类调用
    func ShowCamera(camera Camera) {camera.TakePhoto()camera.RecordVideo()
    }
    func ShowTV(TV VideoPlayer) {TV.PlayVideo()
    }func test3() {smartphone := Smartphone{MobilePhone: MobilePhone{Sound: "beep", Tel: "123456", Text: "hello", Name: "智能手机"},camera:      camera{Take: "take", Record: "record", Name: "智能手机"},TV:          TV{Play: "play", Name: "智能手机"},Name:        "智能手机外部",} //创建一个智能手机实例ShowPhone(&smartphone)ShowCamera(&smartphone)ShowTV(&smartphone)
    }
    

    运行结果:

    智能手机外部 发出 beep 的声响      
    智能手机外部 123456 可以拨打电话       
    智能手机 可以发送信息,内容为 hello
    智能手机 可以 take                 
    智能手机 可以 record               
    智能手机 可以 play   
    

    也就是说,我们创建的这个智能手机接口,会优先使用自己类的方法,其次再去使用继承别人的方法!crazy

案例4:空接口作为万能类型使用

// 下面的函数实现的是一个各种类型都可以调用的函数
func Myfunc(arg interface{}) {fmt.Println("我要和你")fmt.Println(arg)
}func test1() {Myfunc("date")Myfunc(17)Myfunc(13.14)
}

运行结果:

我要和你
date    
我要和你
17      
我要和你
13.14   

在实际应用中,我们通常要根据传入数据的不同类型来进行不同操作。go很贴心的给interface{}配备了“断言”这一功能:

func MyFunc2(arg interface{}) {value, ok := arg.(int) //value用来接收传入类型的值(只有断言成功才能接收到),ok用来接收类型转换的结果是否成功if !ok {fmt.Println("类型转换失败")} else {fmt.Println("类型转换成功")fmt.Printf("传入类型为:%T\n", value)}
}func test2() {MyFunc2("date")MyFunc2(17)MyFunc2(13.14)
}

运行结果:

类型转换失败
类型转换成功   
传入类型为:int
类型转换失败  

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 金融业开源技术 术语
  • 报错module ‘markdown‘ has no attribute ‘version‘解决方法
  • 网络包处理库Scapy: 计算checksum,csum
  • 米壳AI:分享一个轻松保存外网高清原视频的方法!
  • 微信企业微信忽然爆满 怎么清理才干净?一招彻底清理干净垃圾文件
  • 顶点照明渲染路径
  • Spring Boot 注解探秘:@Validated 开启数据验证之旅(上)
  • 数据链路层与ARP协议
  • 数学建模笔记—— 灰色关联分析[GRA]
  • tekton pipeline workspaces
  • 3个办法轻松操作:flac转mp3在线快速完成
  • openVX加速-基本概念和简单代码实现
  • 迈向新一代星地融合的高速测试解决方案
  • 经验笔记:Maven 与 Gradle —— Java 构建工具对比
  • react文件详情
  • 【面试系列】之二:关于js原型
  • CentOS 7 防火墙操作
  • Consul Config 使用Git做版本控制的实现
  • CSS魔法堂:Absolute Positioning就这个样
  • es6(二):字符串的扩展
  • Hexo+码云+git快速搭建免费的静态Blog
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • node 版本过低
  • node学习系列之简单文件上传
  • PHP 7 修改了什么呢 -- 2
  • Protobuf3语言指南
  • Python语法速览与机器学习开发环境搭建
  • XForms - 更强大的Form
  • 从零开始在ubuntu上搭建node开发环境
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 可能是历史上最全的CC0版权可以免费商用的图片网站
  • 前端面试之CSS3新特性
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 用Visual Studio开发以太坊智能合约
  • 正则表达式
  • 《码出高效》学习笔记与书中错误记录
  • ​浅谈 Linux 中的 core dump 分析方法
  • #数学建模# 线性规划问题的Matlab求解
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • (3) cmake编译多个cpp文件
  • (C++二叉树05) 合并二叉树 二叉搜索树中的搜索 验证二叉搜索树
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (MIT博士)林达华老师-概率模型与计算机视觉”
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (二开)Flink 修改源码拓展 SQL 语法
  • (翻译)Quartz官方教程——第一课:Quartz入门
  • (分类)KNN算法- 参数调优
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (四)stm32之通信协议
  • (算法)大数的进制转换
  • (学习日记)2024.01.09
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • (转载)PyTorch代码规范最佳实践和样式指南
  • * 论文笔记 【Wide Deep Learning for Recommender Systems】