【Go语言】面向对象扩展——接口

简单地说 Interface是一组Method的组合,可以通过Interface来定义对象的一组行为。
如果某个对象实现了某个接口的所有方法,就表示它实现了该借口,无需显式地在该类型上添加接口说明。

Interface是一个方法的集合,它里面没有其他类型变量,而且Method只用定义原型 不用实现

①接口定义

1.命名时习惯以"er"结尾,如Printer Reader Writer

2.一个Interface的Method不宜过多,一般0~3个

3.一个Interface可以被任意的对象事项;相应地,一个对象也可以实现多个Interface

示例:

type People struct{Name string
}
type Student struct{PeopleSchool string
}
type Teacher struct{PeopleDepartment string
}
func (p People) SayHi(){}
func (s Student) SayHi(){}
func (t Teacher) SayHi(){}
func (s Student) Study(){}//根据struct的方法提取接口 从而使struct自动实现了该接口
type Speaker interface{SayHi()
}
type Learner interface{SayHi()Study()
}

 

上面的例子中,Speaker接口被对象People,Teacher,Student实现;而Student同时实现了接口Speaker和Learner。

接口组合:

type SpeakLearner interface {SpeakerLearner
}//组合后使得SpeakLearner具有Speaker和Learner的功能

 

空接口:

任何类型都实现了空接口,相当于Java中的Object类

func test(a interface{}){}//该方法可以接受任意类型(int rune float32 struct...)的参数

 

②接口执行机制和接口赋值

首先介绍一种Go语言带接收者(Receiver)的函数机制(下面的两种情况执行结果一样,涉及到struct成员值改变时仍然一样)

情况1:

package mainimport ("fmt"
)type People struct {Name string
}func (p People) SayHi(){ //此处的Receiver是strcutfmt.Println("hello, this is", p.Name)
}
func (p *People) Study(){//此处的Receiver是****structfmt.Printf("%s is studying\n", p.Name)
}
type SpeakLearner interface {SayHi()Study()
}
func main() {people := People{"zhangsan"}//这里的people为People类型
    people.SayHi()people.Study()
}

 

情况2:

func main() {people := &People{"zhangsan"}//这里的people为**People类型,即指针
    people.SayHi()people.Study()
}

 

通过上面的例子可以看出Receiver为People和*People的函数均可被People或者*People两种类型调用,接下来借可能有在调用过程中People与*People之间的转换问题

看下面的例子:

package mainimport ("fmt"
)
type Example struct{Integer1 intInteger2 int
}
func (e Example) Assign(num1 int, num2 int) {e.Integer1, e.Integer2 = num1, num2
}
func (e *Example) Add(num1 int, num2 int) {e.Integer1 +=num1e.Integer2 +=num2
}
func main(){var e1 Example = Example{3,4}e1.Assign(1,1)fmt.Println(e1)e1.Add(1,1)fmt.Println(e1)var e2 *Example = &Example{3,4}e2.Assign(1,1)fmt.Println(e2)e2.Add(1,1)fmt.Println(e2)
}

 

以上程序的执行结果为:

{3,4}
{4,5}
&{3,4}
&{4,5}

 

可以看出实际执行的过程按函数定义前的Receiver类型执行。

对于接口的执行机制:

1.T仅拥有属于T类型的方法集,而*T则同时拥有(T+*T)方法集
2.基于T实现方法,表示同时实现了interface和interface(*T)接口
3.基于*T实现方法,那就只能是对interface(*T)实现接口

type Integer intfunc (a Integer) Less(b Integer) bool {return a < b
}
func (a *Integer) Add(b Integer) {*a += b
}
相应地,我们定义接口LessAdder,如下:
type LessAdder interface {Less(b Integer) boolAdd(b Integer)
}现在有个问题:假设我们定义一个Integer类型的对象实例,怎么将其赋值给LessAdder接口呢?
应该用下面的语句(1),还是语句(2)呢?
var a Integer = 1
var b LessAdder = &a  ... (1)
var b LessAdder = a   ... (2)
答案是应该用语句(1)。原因在于,Go语言可以根据下面的函数:
func (a Integer) Less(b Integer) bool   
即自动生成一个新的Less()方法:
func (a *Integer) Less(b Integer) bool {return (*a).Less(b)
}
这样,类型*Integer就既存在Less()方法,也存在Add()方法,满足LessAdder接口。而从另一方面来说,根据
func (a *Integer) Add(b Integer)
这个函数无法自动生成以下这个成员方法:
func (a Integer) Add(b Integer) {(&a).Add(b)
}
因为(&a).Add()改变的只是函数参数a,对外部实际要操作的对象并无影响,这不符合用
户的预期。所以,Go语言不会自动为其生成该函数。
因此,类型Integer只存在Less()方法,缺少Add()方法,不满足LessAdder接口,故此上面的语句(2)不能赋值。

 

接口赋值举例:

package main
import("fmt"
)
//定义对象People、Teacher和Student
type People struct {Name string
}
type Teacher struct{PeopleDepartment string
}
type Student struct{PeopleSchool string
}
//对象方法实现
func (p People) SayHi() {fmt.Printf("Hi, I'm %s. Nice to meet you!\n",p.Name)
}
func (t Teacher) SayHi(){fmt.Printf("Hi, my name is %s. I'm working in %s .\n", t.Name, t.Department)
}
func (s Student) SayHi() {fmt.Printf("Hi, my name is %s. I'm studying in %s.\n", s.Name, s.School)
}
func (s Student) Study() {fmt.Printf("I'm learning Golang in %s.\n", s.School)
}
//定义接口Speaker和Learner
type Speaker interface{SayHi()
}
type Learner interface{SayHi()Study()
}
func main() {people := People{"张三"}teacher := Teacher{People{"郑智"}, "Computer Science"}student := Student{People{"李明"}, "Yale University"}var is Speaker   //定义Speaker接口类型的变量is = people      //is能存储Peopleis.SayHi()is = teacher     //is能存储Teacheris.SayHi()is = studentis.SayHi()      //is能存储Studentvar il Learneril = student    //Learner类型接口的变量能存储Student
    il.Study()
}

 

执行结果为:

Hi, I'm 张三. Nice to meet you!
Hi, my name is 郑智. I'm working in Computer Science .
Hi, my name is 李明. I'm studying in Yale University.
I'm learning Golang in Yale University.

 

通过这个例子可以 看到(如同Java等语言)接口机制在多态和创建可扩展可重用的代码时的重要作用

③匿名字段和接口转换

若果接口类型S内部嵌入了接口类型T(匿名),则接口匿名字段方法集规则如下:

1.如果S嵌入匿名类型T,则S方法集包含T方法集。
2.如果S嵌入匿名类型*T,则S方法集包含*T方法集(包括Riceiver为T和*T的方法)。
3.如果S嵌入匿名类型T或*T,则*S方法集包含*T方法集(包括Riceiver为T和*T的方法)。(重要)

例如:

package main
import("fmt"    
)type People struct {Name string
}
type S1 struct{People               //S1类型嵌入匿名PeopleDepartment string
}
type S2 struct{*People            //S2类型嵌入匿名*PeopleDepartment string
}
func (p People) Say1() {fmt.Printf("Hi, I'm %s. Say1111\n",p.Name)
}
func (p *People) Say2() {fmt.Printf("Hi, I'm %s. Say2222\n",p.Name)
}
type Speaker interface{Say1()Say2()
}func main() {people := People{"张三"}s1 := S1{People{"郑智"}, "Computer Science"}s2 := S2{&People{"李明"}, "Math"}var is Speaker   is = &people  //*People实现了Speaker接口is.Say1()is.Say2()//is = s1   //S1类型嵌入匿名People  不存在Say2()方法 因而未实现Speaker接口//错误提示: cannot use s1 (type S1) as type Speaker in assignment://S1 does not implement Speaker (Say2 method has pointer receiver)is = s2   //S2类型嵌入匿名*People  因而(p People) Say1()和(p *People) Say2()方法都有 实现了Speaker接口is.Say1()is.Say2()is = &s1  //S1类型嵌入匿名People  *S1 实现了Speaker接口is.Say1()is.Say2()is = &s2 //S2类型嵌入匿名*People  *S2 实现了Speaker接口is.Say1()is.Say2()}

 

 执行结果为:

Hi, I'm 张三. Say1111
Hi, I'm 张三. Say2222
Hi, I'm 李明. Say1111
Hi, I'm 李明. Say2222
Hi, I'm 郑智. Say1111
Hi, I'm 郑智. Say2222
Hi, I'm 李明. Say1111
Hi, I'm 李明. Say2222

 

从而证明了匿名字段方法集的3条规则。

接口转换类似于说是接口继承规则 可认为是实现复杂接口(方法多)向简单接口(方法少)转换,其中简单接口中的方法在复杂接口中均有声明 。例如:

package main
import("fmt"
)
type People struct {Name string
}
type Student struct{PeopleSchool string
}
func (p People) GetPeopleInfo() {fmt.Println(p)
}
func (s Student) GetStudentInfo() {fmt.Println(s)
}
type PeopleInfo interface{GetPeopleInfo()
}
type StudentInfo interface{GetPeopleInfo()GetStudentInfo()
}
func main() {var is StudentInfo = Student{People{"李明"}, "Yele University"}is.GetStudentInfo()is.GetPeopleInfo()var ip PeopleInfo = isip.GetPeopleInfo()///ip.GetStudentInfo()  note:ip.GetStudentInfo undefined
}

 

④接口类型推断:Comma-ok断言和Switch测试

 利用接口类型推断可以 反向知道接口类型变量里面实际保存的是哪一种类型的对象。

Go语言中,常用两种方法可以进行接口类型推断,即Comma-ok断言和Switch测试

Comma-ok断言使用格式如下

value,ok = element.(T)

 

用法示例:

//利用Comma-ok断言进行接口类型推断
package main
import("fmt"
)type People struct{Name stringAge int
}//定义空接口用于存储任意类型数据类型
type Object interface{}func main() {people := People{"张三", 20}objs := make([]Object, 4)objs[0], objs[1], objs[2], objs[3] = 1, true, "Hello", peoplefor index, element := range objs{if value, ok := element.(int); ok{fmt.Printf("objs[%d]类型是int,value=%d\n", index, value)}else if value, ok := element.(bool); ok{fmt.Printf("objs[%d]类型是bool,value=%v\n", index, value)}else if value, ok := element.(string); ok{fmt.Printf("objs[%d]类型是string,value=%s\n", index, value)}else if value, ok := element.(People); ok{fmt.Printf("objs[%d]类型是Peole,value=%v\n", index, value)}else{fmt.Printf("objs[%d]类型未知\n", index)}}
}

 

结果是这样的:

objs[0]类型是int,value=1
objs[1]类型是bool,value=true
objs[2]类型是string,value=Hello
objs[3]类型是Peole,value={张三 20}

 

使用Switch测试判断接口类型,程序结构更加简洁,示例如下(只修改了示例中的main函数):

func main() {people := People{"张三", 20}objs := make([]Object, 4)objs[0], objs[1], objs[2], objs[3] = 1, true, "Hello", peoplefor index, element := range objs{switch value := element.(type){case int:fmt.Printf("objs[%d]类型是int,value=%d\n", index, value)case bool:fmt.Printf("objs[%d]类型是bool,value=%v\n", index, value)case string:fmt.Printf("objs[%d]类型是string,value=%s\n", index, value)case People:fmt.Printf("objs[%d]类型是Peole,value=%v\n", index, value)default:fmt.Printf("objs[%d]类型未知\n", index)}}
}

 

执行结果Comma-ok方法相同,但是程序简洁了许多。

转载于:https://www.cnblogs.com/Mike-zh/p/3787679.html

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

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

相关文章

PHPCMS代码生成器

软件截图 软件说明 软件名称&#xff1a;PHPCMS代码生成器 版本&#xff1a;v1.0 开发语言&#xff1a;易语言 软件说明&#xff1a;此软件是用于对PHPCMS模板进行编辑制作的高效代码生成器&#xff0c;大部分的操作代码在此软件内都可实现&#xff0c;欢迎使用。 火眼检测报告…

18款 非常实用 jquery幻灯片图片切换

1、jquery图片滚动仿QQ商城带左右按钮控制焦点图片切换滚动 jquery图片特效制作仿腾讯QQ商城首页banner焦点图片轮播切换效果&#xff0c;带索引按钮控制和左右按钮控制图片切换。 查看演示>> 下载地址 2、jquery图片切换插件制作图片层叠缩放展示效果 jquery图…

全国电视直播v1.0

软件截图 软件说明 软件名称&#xff1a;电视直播 版本&#xff1a;v1.0 开发语言&#xff1a;易语言 软件说明&#xff1a;此软件可以观看几乎全国各地的电视台&#xff0c;无如播放不流畅&#xff0c;可以切换左侧的播放源&#xff0c;欢迎使用。 下载地址&#xff1a;http…

iOS UIModalPresentationFormSheet风格下的键盘隐藏

1. 在UIModalPresentationFormSheet&#xff08;iPad device, without a UINavigationController&#xff09;下的视图中&#xff0c;如果使用 [inputView resignFirstResponder]; 是不能把Keyboard收起的&#xff0c;需要使用以下的方式&#xff1a; A&#xff1a; try { Clas…

迅雷会员帐号获取器

软件截图 软件说明 软件名称&#xff1a;迅雷会员获取器 版本&#xff1a;v1.0 开发语言&#xff1a;易语言 软件说明&#xff1a;此在左上角的下拉列表框中选择来源&#xff0c;单击“获取帐号”即可在下方的列表中出现迅雷会员的账号和密码&#xff0c;如果获取失败&#x…

HTML代码转换编辑器

软件截图 软件说明 软件名称&#xff1a;HTML代码转换编辑器 版本&#xff1a;v1.0 开发语言&#xff1a;C# .Net framework3.5 下载地址&#xff1a;http://download.csdn.net/detail/lecepin/9417174 博客名称&#xff1a;王乐平博客 博客地址&#xff1a;http://blog.le…

系统时间校准工具

软件截图 软件说明 软件名称&#xff1a;系统时间校准 版本&#xff1a;v1.0 开发语言&#xff1a;易语言 软件说明&#xff1a;方便电脑系统时间不正确&#xff0c;每次启动时间都重置等时间不正确的情况&#xff0c;提供时间校准的方便。校准时间均为世界标准时间同步网络…

localtime 和 localtime_r

---恢复内容开始--- 上程序&#xff1a; [c-sharp] view plaincopyprint?#include <cstdlib> #include <iostream> #include <time.h> #include <stdio.h> using namespace std; int main(int argc, char *argv[]) { time_t tNow tim…

系统关机助手

软件截图 软件说明 软件名称&#xff1a;系统关机助手 版本&#xff1a;v1.0 开发语言&#xff1a;易语言 软件说明&#xff1a;方便关机、重启、注销等操作的小软件&#xff0c;欢迎使用。 下载地址&#xff1a;http://download.csdn.net/detail/lecepin/9418082 博客名称&a…

poj1703

题目大意&#xff1a;一共有两个类&#xff0c;两种操作D X Y表示X Y在不同的类里面&#xff0c;A X Y 询问X Y之间的关系(未知&#xff0c;相同&#xff0c;不同) 分析&#xff1a;简单带权并查集&#xff0c;D[i]表示与i对立的类&#xff0c;维护好这个变量就可以了。 1 #inc…

SEO原创文章制作器

软件截图 软件说明 软件名称&#xff1a;SEO原创文章制作器 版本&#xff1a;v1.0 开发语言&#xff1a;易语言 软件说明&#xff1a;SEO福利工具&#xff0c;可直接从网上将内容复制到本软件&#xff0c;然后生成文字不一样但意思一样的原创文本。 此软件可以设置原创级别。…

水星MW300R v2 路由器刷DD-Wrt 小记

家里有一个水星MW300R v2 的路由器&#xff0c;之前在这个路由器上刷过OpenWrt&#xff08;可参照我这个博文&#xff1a;http://wlpblog.blog.163.com/blog/static/217614257201511252554386/&#xff09;&#xff0c;后来刷回了原系统。最近发觉这个路由器系统有点问题&#…

ns,ui

转&#xff1a;http://blog.csdn.net/fengsh998/article/details/8842885 Cocoa框架是iOS应用程序的基础&#xff0c;了解Cocoa框架&#xff0c;对开发iOS应用有很大的帮助。 1、Cocoa是什么&#xff1f; Cocoa是OS X和 iOS操作系统的程序的运行环境。 是什么因素使一个程序成为…

试题集—— 算法提高 学霸的迷宫

题目&#xff1a; 问题描述学霸抢走了大家的作业&#xff0c;班长为了帮同学们找回作业&#xff0c;决定去找学霸决斗。但学霸为了不要别人打扰&#xff0c;住在一个城堡里&#xff0c;城堡外面是一个二维的格子迷宫&#xff0c;要进城堡必须得先通过迷宫。因为班长还有妹子要陪…

kerberos 主从安装

hadoop 安全应用kerberos &#xff0c;搭建KDC使其高可用操作系统 ubuntu 12.04 Kerberos Server ##################### 规范介绍 #######################################Realm: BJPUC1.COM --> 建议大写Primary KDC: nn1.bjpuc1.com --> 建议用域名 192.168.88.16Se…

Android手机调整/data分区和/sdcard分区

现在手上有一块E160K的手机&#xff0c;手机内存是32GB的&#xff0c;由于之前对这个手机刷机不当&#xff0c;造成用户分区20GB多&#xff0c;而内置SD仅仅只有5GB多&#xff0c;造成了严重的资源浪费&#xff0c;所以我需要把这两个分区的大小调整一下。 问题分析&#xff1a…

自定义的事件管理器

自定义的事件管理器 周银辉 大多数框架下都提供了事件管理器的&#xff0c;但不使用框架时为了让事件发送者和事件接收者之间解耦&#xff0c;就可以如下写个简单的 public enum EventAdministratorEventTypes{ApplicationStartup, //在这里添加你需要的事件}public class Even…

高级堆排序

今天在一个OJ上做了一个叫“Advanced Heap Sort”的题&#xff0c;题的解决算法没什么难的&#xff0c;但是对时间复杂度有要求&#xff0c;用正常的算法实现&#xff0c;都会超时&#xff0c;所以我就把这个题拿过来分享一下。 问题&#xff1a; 题目内容&#xff1a; 有两个序…

可以创建专业的客户端/服务器视频会议应用程序的音频和视频控件LEADTOOLS Video Conferencing SDK...

LEADTOOLS Video Streaming Module控件为您创建一个自定义的视频会议应用程序和工具提供所有需要的功能。软件开发人员可以使用Video Streaming Module SDK&#xff0c;通过一些不同的配置来创建一些客户端/服务器应用程序。例如&#xff0c;如果有一个服务器需要向多个客户端发…

Joseph Problem(解约瑟夫问题)

今天在一个OJ上做了一个Joseph Problem&#xff08;解约瑟夫问题&#xff09;的题&#xff0c;题目不难&#xff0c;直接用循环链表模拟实际操作即可完成&#xff0c;但是用此种方法的时间太长&#xff0c;超时&#xff0c;所以我就用了一个大家对这类问题比较常用的解法——数…