Go 接口和多态

news2024/9/23 11:25:11

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

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

代码如下:

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)
}
//可以看到调用起来还是很简单的,直接给父类结构体的属性赋值,然后调用减法的方法就行

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

一、 什么是接口

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

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

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

二、 接口定义

接口定义的语法如下:

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

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

//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)
}

具体的调用如下:

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()
}

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

接口的命名习惯以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)
}

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

四、多态案例

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

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

四、 接口继承与转换

接口也可以实现继承:

// 先定义接口  一般以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

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

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("大碗面")
}

五、 空接口

空接口(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   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/986465.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

线性空间和线性变化

目录 考点一、线性空间的基与维数 1、线性空间 2、基底 3、子空间&#xff08;线性子空间&#xff09; ​编辑4、生成子空间 &#xff08;1&#xff09;、v1 n v2 &#xff08;2&#xff09;、v1 v2 5、求和子空间的方法 6、维数定理 7、例题 &#xff08;1&#xf…

解锁前端Vue3宝藏级资料 第四章 VUE常用 UI 库 2 ( ailwind 后台框架)

4.5 ailwind 上面介绍的都是国内比较优秀的UI框架&#xff0c;现在我们在介绍一款国外比较流行的CSS UI框架ailwind 。官方网站https://tailwindcss.com/docs/guides/vite#vue CSShttps://flowbite.com/docs/getting-started/introduction/ 。这个ailwind 架构需要自己去写一些…

《基于区块链的数据资产评估实施指南》技术研讨会成功召开

2023年9月1日&#xff0c;《基于区块链的数据资产评估实施指南》&#xff08;以下简称《指南》&#xff09;技术研讨会在深圳召开&#xff0c;竹云科技作为主要参编单位出席此次研讨会。 中国科协决策咨询首席专家王春晖&#xff0c;中国社会科学院博士于小丽&#xff0c;中国…

Leetcode 易错题整理(三)73. 77. 78. 81. 90. 95.105. 130.

73. 矩阵置零 给定一个 *m* x *n* 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法**。** 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]]笨方法&…

在很多公司里面会使用打tag的方式保留版本

&#xff1a;git tag|grep "xxx-dev“等分支来查看 2&#xff1a;git cherry-pick XXXXX 然后就是查看有冲突这些 git status 会出现相关的异常 然后解决相关的冲突 git add . git cherry-pick --continue git push XXX HEAD:refs/for/XXX 第一&#xff1a;git ta…

Excel自学三部曲_Part3:Excel工作场景分析实战

文章目录 一、Excel工作场景与知识点总结1. 学哪个Excel?&#xff08;1&#xff09;学习哪个版本的Excel?&#xff08;2&#xff09;Excel和WPS到底学哪个&#xff1f; 2. 怎么用Excel?&#xff08;1&#xff09;低量级数据的存储&#xff08;2&#xff09;一次性的数据处理…

解决使用torchstat时报错“AttributeError: module ‘numpy‘ has no attribute ‘long‘”等问题

背景 首先直接使用pip install torchstat安装。 使用torchstat查看模型参数和flops&#xff1a; from torchstat import stat stat(model.to(cpu), (2, 32, 32)) # 这里第二个参数取决于自己的模型输入大小报错1 运行报错如下&#xff1a; 核心错误为&#xff1a; “Attri…

uniapp分包 解决分多个包的问题

1. 分包可以分很多个, 但是在"optimization": { "subPackages": true } 里面只能写一个, 2. 想分多个包 , 在 pages.json 里面 的 subPackages 里面继续加 第三个 第四个即可 3. 保存之后 创建页面就可以看见多个包了

异常的顶级理解

目录 1.异常的概念与体系结构 1.1异常的体系结构 1.2异常的举例 1.3错误的举例 2.异常的分类 2.1编译时异常 2.2运行时异常 3.异常的处理 3.1异常的抛出throw 3.2try-catch捕获并处理 3.3finally 3.4 异常声明throws 4.自定义异常类 1.异常的概念与体系结构 1.1异常的…

BMS电池管理系统——什么是BMS(一)

BMS电池管理系统 文章目录 BMS电池管理系统前言一、BMS是什么&#xff1f;二、BMS的主要功能模块1.采样及测量功能2.状态估计及预测功能3.控制及管理功能4.通讯和诊断功能 总结 前言 作为一名电气专业的学生&#xff0c;大学里学了很多嵌入式相关的知识&#xff0c;首先要明确…

便捷高效的一键发布成绩

今天我要给大家推荐一个非常实用的教育工具——易查分。作为一名教育工作者&#xff0c;我深知每次发布成绩都是一项繁琐而重要的任务。然而&#xff0c;有了易查分&#xff0c;这一切都变得轻松高效起来。让我来告诉各位老师&#xff0c;易查分是如何实现一键发布成绩的&#…

Xilinx IDDR与ODDR原语的使用

文章目录 ODDR原语1. OPPOSITE_EDGE 模式2. SAME_EDGE 模式 ODDR原语 例化模板&#xff1a; ODDR #(.DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE" .INIT(1b0), // Initial value of Q: 1b0 or 1b1.SRTYPE("SYNC…

Scrapy的基本介绍、安装及工作流程

一.Scrapy介绍 Scrapy是什么&#xff1f; Scrapy 是用 Python 实现的一个为了爬取网站数据、提取结构性数据而编写的应用框架(异步爬虫框架) 通常我们可以很简单的通过 Scrapy 框架实现一个爬虫&#xff0c;抓取指定网站的内容或图片。 Scrapy使用了Twisted异步网络框架&…

【LeetCode题目详解】第九章 动态规划 part05 1049. 最后一块石头的重量 II 494. 目标和 474.一和零(day43补)

本文章代码以c为例&#xff01; 一、力扣第1049题&#xff1a;最后一块石头的重量 II 题目&#xff1a; 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎…

C++学习记录——삼십삽 STL空间配置器

文章目录 1、概念2、使用3、容器上的体现 1、概念 我们先看malloc&#xff0c;malloc是创建在堆上的&#xff0c;虽然malloc可以申请内存&#xff0c;但也有限制&#xff0c;windows下用VirtualAlloc可以直接向堆申请内存&#xff0c;Linux中则是brk&#xff0c;不过这两个效率…

HCIA自学笔记01-传输介质

通信网络除了包含通信设备本身之外&#xff0c;还包含连接这些设备的传输介质&#xff0c;如同轴电缆、双绞线和光纤等。不同的传输介质具有不同的特性&#xff0c;这些特性直接影响到通信的诸多方面&#xff0c;如线路编码方式、传输速度和传输距离等。 简单网络&#xff1a;…

✔ ★算法基础笔记(Acwing)(一)—— 基础算法(20道题)【java版本】

基础算法 一、快速排序1. 快速排序例题2. 第k个数( 快速选择 ) ✔ ✔1.31★快排二刷总结( 4点 ) 二、归并排序1. 归并排序模板题 ✔ ✔1.31★二刷总结 ★2. 逆序对的数量 ✔ ✔1.31★二刷总结 三、二分1. 数的范围 ✔1.31★二刷总结(mid > x 则是 输出最左边一个)第一个大于…

【操作系统】聊聊Linux内存工作机制

内存主要是用来存储系统和应用程序的指令、数据、缓存等 内存映射 内存是需要安全机制保护的&#xff0c;所以只有内核才可以直接访问物理内存。进程如果要访问内存需要通过独立的虚拟地址空间。 虚拟地址空间其实包含两部分。一部分是内核空间&#xff0c;另一部分就是用户…

搭建RabbitMQ消息服务,整合SpringBoot实现收发消息

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;3年JAVA全栈开发经验&#xff0c;专注JAVA技术、系统定制、远程指导&#xff0c;致力于企业数字化转型&#xff0c;CSDN博客专家&#xff0c;蓝桥云课认证讲师。 目录 一、前言1.1 什么是消息队列1.2 RabbitMQ 是什么1.…

【AI】机器学习——线性模型(线性回归)

线性模型既能体现出重要的基本思想&#xff0c;又能构造出功能更加强大的非线性模型 文章目录 3.1 线性模型3.1.1 数据3.1.2 目标/应用 3.2 线性回归3.2.1 回归模型历史3.2.2 回归分析研究内容回归分析步骤 3.2.3 回归分析分类3.2.4 回归模型3.2.5 损失函数梯度下降法一元回归模…