go语言之接口
一:接口的基本概念
1 接口声明
接口字面量,接口命名类型,接口声明使用interface关键字。
1)接口字面量类型声明语法如下:
- interface{
- methodSignature1
- methodSignature1
- }
2)接口命名类型使用type关键字声明
- type interfaceName interface {
- MethodSignature1
- MethodSignature2
- }
- 接口定义大括号内可以是方法声明的集合,也可以嵌入另一个接口类型匿名字段,还可以是二者的混合。
- 接口支持嵌入匿名接口宇段,就是一个接口定义里面可以包括其他接口, Go编译器会自动进行展开 理,
- type Reader interface {
- Read(p []byte ) (n int , err error)
- }
- type Writer interface {
- Write(p []byte) (n int, err error)
- }
- type ReadWriter interface {
- Reader
- Writer
- }
- type ReadWr ter interface {
- Reader
- Wr te(p []byte) (n int, err error)
- }
- type ReadWriter interface {
- Read(p []byte) (n int, err error)
- Write(p []byte) (n nt err error)
- }
3)方法声明
- MethodName (InputTypeList)OutputTypeList
4)声明新接口类型的特点
- (I)接口的命名一般以“er ”结尾
- ()接口定义的内部方法声明不需要 func 引导。
- ()在接口定义中,只有方法声明没有方法实现。
5) 接口的定义与实现
- package main
- import (
- "fmt"
- )
- type Humaner interface {
- //方法
- Say()
- }
- //学生结构体
- type Student struct {
- name string
- score int
- }
- func (s*Student) Say() {
- fmt.Println("Student[%s,%d]瞌睡不断\n",s.name,s.score)
- }
- type Teacher struct {
- name string
- group string
- }
- func (t *Teacher) Say() {
- fmt.Println("Teacher[%s,%s] 诲人不倦\n",t.name,t.group)
- }
- //自定义类型
- type Mystr string
- func (str Mystr) Say() {
- fmt.Println("Mystr[%s] 统治醒醒,还有个bug\n",str)
- }
- //参数为接口类型
- func Whosay(i Humaner) {
- i.Say()
- }
- func main(){
- s :=&Student{"学生",}
- t :=&Teacher{"老师","GO语言"}
- var tmp Mystr="字符串"
- s.Say()
- t.Say()
- tmp.Say()
- //多态,条用同一接口不同的表现
- Whosay(s)
- Whosay(t)
- Whosay(tmp)
- //make()
- x :=make([]Humaner,)
- x[],x[],x[] = s,t,tmp
- for _,value :=range x{
- value.Say()
- }
- }
接口的继承
- package main
- import "fmt"
- //定义接口
- type Humaner interface {
- //方法
- Say()
- }
- type Personer interface {
- //相当于写了say() 方法的继承
- Humaner
- //唱歌
- Sing(lyrics string)
- }
- type Student struct {
- name string
- score int
- }
- func (s *Student) Say() {
- fmt.Printf("Student[%s,%d] 瞌睡不断\n",s.name,s.score) //Student[学生,80] 瞌睡不断
- }
- func (s *Student) Sing(lyrics string){
- fmt.Printf("Student sing[%s]!!\n",lyrics) //Student sing[葫芦娃]!!
- }
- func main() {
- s := &Student{"学生",}
- //调Personer方法
- var p Personer
- p = s
- p.Say()
- p.Sing("葫芦娃")
- }
2 接口初始化
- 单纯地声明一个接口变量没有任何意义,接口只有被初始化为具体的类型时才有意义。接口作为
一个胶水层或抽象层,起到抽象和适配的作用 。没有初始化的接口变量,其默认值是 nil。
3 接口绑定具体类型的实例的过程称为接口初始化。接口变量支持两种直接初始化方法
1)实例赋值接口
- 如果具体类型实例的方法集是某个接口的方法集的超集,则称该具体类型实现了接口,可
- 以将该具体类型的实例直接赋值给接口类型的变 ,此时编译器会进行静态的类型检查。接口
- 被初始化后,调用接口的方法就相当于调用接口绑定的具体类型的方法,这就是接口调用的语义。
2)接口变量赋值接口变量
- 已经初始化的接口类型变量a直接赋值给另一种接口变量b ,要求b的方法集是a的方法即
- 的子集 此时 Go 编译器会在编译时进行方法集静态检查 这个过程也是接口初始化的一种
- 方式,此时接口变量 绑定的具体实例是接口变量 绑定的具体实例的副本。
- file ,_ := os .OpenFile (” notes.txt”, os.O_RDWR |os.O CREATE , )
- var rw io .ReadWriter = file
- //io.ReadWriter 口可以直接赋位给 io.Writer接口变量
- var w o.Writer = rw
4 接口方法的调用
- 接口方法调用和普通的函数调用是有区别的。接口方法调用的最终地址是在运行期决定的,
- 将具体类型变量赋值给接口后,会使用具体类型的方法指针初始化接口变量,当调用接口变量的方法时,
- 实际上是间接地调用实例的方法。接口方法调用不是 种直接的调用,有 定的运行时开销
直接调用禾初始化的接口变 的方法会产生 panic 。
- package main
- type printer interface {
- Print()
- }
- type S struct {}
- func (s S) Print() {
- println("print")
- }
- func main() {
- var i printer
- //没有初始化的接口调用其他方法会产生panic
- //必须初始化
- i = S{}
- i.Print()
- }
5 接口动态类型和静态类型
1)动态类型
- 接口绑定的具体实例的类型称为接口的动态类型。接口可以绑定不同类型的实例,所以接
- 口的动态类型是随着其绑定的不同类型实例而发生变化的。
2) 静态类型
- 接口被定义时, 其类型就已经被确定 这个类型 接口的静态类型。接口的静态类型在其
- 定义 就被确定,静态类型的本质特征就是接口的方法签名集合。两个接口如果方法签名集合
- 相同(方法的顺序可以不同),则这两个接口在语义上完全等价,它们之间不需要强制类型转换就可以相互赋值。
- 原因是 Go 编译器校验接口是否能赋值,是比较二者的方法集,而不是看具体接口类型名。
二: 接口运算
1 语法:
- i.(TypeNname)
- i必须是接口变 ,如果是具体类型变量,则编译器会报 on interface type xxx on left,
- TypeNname 可以是接口类型名,也可以是具体类型名。
2 接口查询的两层含义
- (1)如果 TypeNname 是一个具体类型名,则类型断言用于判断接口变量 绑定的实例类
- 型是否就是具体类型 TypeNname
- (2)如果 TypeName 是一个接口类型名,则类型断言用于判断接口变量 绑定的实例类型
- 是否同时实现了 TypeName 接口。
3 接口断言的两种语法表现
直接赋值模式
- o := i.(TypeName)
语义分析:
- () TypeNam 是具体类型名,此时如果接 绑定的实例类型就是具体类型 TypeName,
- 变量 。的类型就是 TypeName 变量。的值就是接口绑定的实例值的副本(当然实例可能是
- 指针值,那就是指针值的副本)
- () TypeName 是接口类型名 如果接口i绑定的实例类型满足接口类型 TypeName ,则变量o
- 的类型就是接口类型 TypeName,o底层绑定的具体类型实例是i绑定的实例的副本(当然实例可能是指针值,那就是指针值的副本〉。
- ()如果上述两种情况都不满足, 则程序抛 panic
示例
- package main
- import "fmt"
- type Inter interface {
- Ping()
- Pang()
- }
- type Anter interface {
- Inter
- String()
- }
- type St struct {
- Name string
- }
- func (St) Ping() {
- println("ping")
- }
- func (*St) Pang() {
- println("pang")
- }
- func main() {
- st := &St{"andes"}
- var i interface{}=st
- //判断i绑定的实例是否实现了接口类型Inter
- o :=i.(Inter)
- o.Ping()
- o.Pang()
- //如下语句会引发panic,因为i没有实现接口Anter
- //p :=i.(Anter)
- //p.String()
- //判断 i绑定的实例是否就是具体类型St
- s := i.(*St)
- fmt.Printf("%s",s.Name)
- }
4 comma,ok 表达模式如下
- if o,ok :=i.(TypeName);ok{
- }
语法分析
- ()TypeName是具体类型名,此时如果接口i绑定的实例类型就是具体类型TypeName,则ok为true变量。变量o的类型就是TypeName,
- 变量o的值就是接口绑定的实例值的副本(当然实例可能是指针值,那就是指针值的副本)
- ()TypeName是接口类型名,此时如果接口i绑定的实例类型满足接口类型TypeName,则ok为true,变量o的类型就是接口类型
TypeName,o底层绑定的具体类型实例是i绑定的实例的副本(当然实例可能是指针值,那就是指针值的副本)。- ()如果上述两个都不满足,则 ok为 false 变量o是TypeName 类型的“零值”,此种条
- 件分支下程序逻辑不应该再去引用o,因为此时的o没有意义
示例:
- package main
- import (
- "fmt"
- )
- type Inter interface {
- Ping()
- Pang()
- }
- type Anter interface {
- Inter
- String()
- }
- type St struct {
- Name string
- }
- func (St) Ping(){
- println("ping")
- }
- func (*St) Pang(){
- println("pang")
- }
- func main(){
- st := &St{"andes"}
- var i interface{} = st
- //判断i绑定的实例是否实现了接口类型Inter
- if o,ok := i.(Inter);ok{
- o.Ping() //ping
- o.Pang() //pang
- }
- if p,ok := i.(Anter);ok{
- //i没有实现接口Anter,所以程序不会执行到这里
- p.String()
- }
- //判断i 绑定的实例是否就是具体类型St
- if s,ok := i.(*St);ok{
- fmt.Printf("%s",s.Name) //andes
- }
- }
5 类型查询
语法格式:
- switch v :=工. (type) {
- case typel :
- xx xx
- case type2 :
- xx xx
- default :
- xx xx
语义分析:
- 语义:
- 查询一个接口变量底层绑定的底层变量的具体类型是什么,
- 查询接口变量绑定的底层变量是否实现了其他接口
1)i 必须是接口类型
- 描述:
- 具体类型实例的类型是静态的 在类型声明后就不再变化,所 具体类型的变量不存在类
- 型查询 类型查询一定是对一个接口变量进行操作。也 就是说,上文中的i必须是接口变
- 如果 是未初始 接口变量,则的值是nil 。
- var i io.Reader
- switch v := i.(type) { //此处i是为未初始化的接口变量,所以v为nil
- case nil :
- fmt.Printf( " %T\n ”,v ) //<nil>
- default :
- fmt.Printf (”default”)
- }
( 2 ) case 字句后面可 m~ 非接口类型名,也可以跟接口类型名,匹配是按照 case 子句的
顺序进行的。
- 如果 case 后面是一个接口类型名,且接口变量 绑定的实例类型实现了该接口类型的方法
- ,则匹配成功,v的类型是接口类型,v底层绑定的实例是 绑定具体类型实例的副本。
示例:
- package main
- import (
- "io"
- "log"
- "os"
- )
- func main() {
- f,err := os.OpenFile ("notes.txt",os.O_RDWR|os.O_CREATE, )
- if err != nil {
- log.Fatal(err)
- }
- defer f.Close()
- var i io.Reader = f
- switch v :=i.(type) {
- //i的绑定的实例是*osFile类型,实现 io.ReadWriter接口,所以case匹配成功
- case io.ReadWriter:
- //v是io.ReadWriter 接口类型,所以可以调用Write方法
- v.Write( []byte ("io.ReadWriter\n" ))
- //由于上一个case 已经匹配,就算这个case 也匹配,也不会走到这里
- case *os.File:
- v.Write ([]byte ("*os.File\n"))
- v.Sync ()
- default:
- return
- }
- }
- 如果case后面跟着多个类型,使用逗号分隔,接口变量i绑定的实例类型只要和其中一个类型匹配,
- 则直接使用o赋值给 v,相当于v := o 这个语法有点奇怪,按理说编译器不应该允许这种操作,
- 语言实现者可能想让 type switch 语句和普通的 sw itch 语句保持一样的语法规则,允许发生这种情况。
- package main
- import (
- "fmt"
- "io"
- "log"
- "os"
- )
- func main(){
- f,err := os.OpenFile("notes1.txt",os.O_RDWR|os.O_CREATE,)
- if err != nil {
- log.Fatal(err)
- }
- defer f.Close()
- var i io.Reader = f
- switch v := i.(type) {
- //多个类型,f满足其中任何一个就算匹配
- case *os.File,io.ReadWriter:
- //此时相当于执行v :=i ,v和i是等价的,使用v没有意义
- if v==i{
- fmt.Println(true) //true
- }
- default:
- return
- }
- }
6 标准库的使用
格式:
- switch i := i.(type) {
- }
类型查询和类型断言
- ()类型查询和类型断言具有相同的语义,只是语法格式不同。 二者都能判断接口变量绑
- 定的实例的具体类型,以及判断接口变量绑定的实例是否满足另一个接口类型。
- ()类型查询使用 case 字句一次判断多个类型,类型断言一次只能判断一个类型,
- 当然类型断言也可以使用 if else if 语句达到同样的效果
7 接口优点和使用形式
接口优点
- (1)解祸:复杂系统进行垂 和水平的分割是常用的设计手段,在层与层之间使用接口进
- 行抽象和解辑是 种好的编程策略 Go 的非侵入式的接口使层与层之间的代码更加干净,
- 具体类型和实现的接口之间不需要显式声明,增加了接口使用的自由度
- (2)实现泛型:由于现阶段Go语言还不支持泛型,使用空接口作为函数或方法参数能够用在需要泛型的场景中
接口的使用形式
- ()作为结构 嵌字段。
- ()作为函数或方法的形参。
- ()作为函数或方法的返回值。
- ()作为其他接口定义的嵌入宇段。
三: 空接口
- 概述:
- 没有任何方法的接口,我们称之为空接 。空接口表示为 interface{}
- 用途:
- 空接口和泛型
- Go 语言没有泛型, 如果一个函数需要接收任意类型的参数, 则参数类型可以使用空接口,这是弥补没有泛型的一种手段
- //典型的就是 fmt 标准 里面的 print 函数
- func Fprint (w io.Writer, a . . . interface(}) (n int, err error)
- 空接口和反射
- 空接口是反射实现 基础 反射库就是将相关具体的类型转换并赋值给空接 后才去处理,
1 空接口和nil
空接口不是真的为空,接口有类型和值两 概念
示例
- package main
- import (
- "fmt"
- )
- type Inter interface {
- Ping()
- Pang()
- }
- type St struct {}
- func (St) Ping(){
- println("ping")
- }
- func (*St) Pang(){
- println("pang") //pamg
- }
- func main(){
- var st *St = nil
- var it Inter = st
- fmt.Printf("%p\n",st) //0x0
- fmt.Printf("%p\n",it) //0x0
- if it !=nil {
- it.Pang()
- //下面的语句会导致 panic
- //方法转换为函数调用,第 一个参数是St类型,由于 St是nil ,无法获取指针所指的
- //对象佳,所以导致 panic
- //it.Ping
- }
- }
comma-ok断言
- package main
- import (
- "fmt"
- )
- //空接口
- type Element interface {}
- type Person struct {
- name string
- age int
- }
- func main() {
- //3容量的切片
- list := make([]Element,)
- list[] = //int
- list[]="Hello" //string
- list[] = Person{"zhangsan",}
- for index,element := range list {
- //类型断言: value,ok =element,(T)
- if value,ok :=element.(int);ok {
- fmt.Printf("list[%d]是int类型,值是%d\n",index,value) //list[0]是int类型,值是1
- }else if value,ok := element.(string);ok {
- fmt.Printf("list[%d]是string类型,值是%s\n",index,value) //list[1]是string类型,值是Hello
- }else {
- fmt.Printf("list[%d]是其他类型\n",index) //list[2]是其他类型
- }
- }
- }
switch 接口测试
- package main
- import "fmt"
- //空接口
- type Element interface{}
- type Person struct {
- name string
- age int
- }
- func main() {
- list := make([]Element, )
- list[] = //int
- list[] = "Hello" //string
- list[] = Person{"zhangsan", }
- for index,element := range list{
- switch value := element.(type) {
- case int :
- fmt.Printf("list[%d]是int类型,值是%d\n",index,value)
- case string:
- fmt.Printf("list[%d]是string类型,值是%s\n",index,value)
- default:
- fmt.Printf("list[%d]是其他类型\n",index)
- }
- }
- }
四: 接口的内部实现(这个涉及底层很多东西,我不会)
go语言之接口的更多相关文章
- R语言数据接口
R语言数据接口 R语言处理的数据一般从外部导入,因此需要数据接口来读取各种格式化的数据 CSV # 获得data是一个数据帧 data = read.csv("input.csv" ...
- [日常] Go语言圣经--接口约定习题
Go语言圣经-接口1.接口类型是对其它类型行为的抽象和概括2.Go语言中接口类型的独特之处在于它是满足隐式实现的3.Go语言中还存在着另外一种类型:接口类型.接口类型是一种抽象的类型4.一个类型可以自 ...
- FFI (语言交互接口(Foreign Function Interface))
FFI(Foreign Function Interface)是用来与其它语言交互的接口, 在有些语言里面称为语言绑定(language bindings), Java 里面一般称为 JNI(Java ...
- C语言原子接口与实现
原子是一个指向唯一的.不可变的0个或任意多个字节序列的指针,大多数原子都是指向以空字符结束的字符串,但是任何一个指向任意字节序列的指针都可以使原子.任何原子只能出现一次.如果两个原子指向同一个内存单元 ...
- c语言调试接口
http://blog.chinaunix.net/uid-10106787-id-2985587.html 在C语言程序设计中,常会出现各种各样的bug:段错误.参数异常等等.我们需要尽快定位错误, ...
- Java语言Socket接口用法详解
Socket接口用法详解 在Java中,基于TCP协议实现网络通信的类有两个,在客户端的Socket类和在服务器端的ServerSocket类,ServerSocket类的功能是建立一个Serve ...
- Go语言的接口
一.接口的定义和好处 我们都知道接口给类提供了一种多态的机制,什么是多态,多态就是系统根据类型的具体实现完成不同的行为. 以下代码简单说明了接口的作用 package main import ( &q ...
- go语言学习-接口
Go语言中虽然没有传统面向对象语言中类.集成的概念,不过提供了接口的支持,可以使用接口来使用一些面向对象的特性. 在 go 语言中,的接口有下面几个特点: 可以包含0个或多个方法的签名 只定义方法的签 ...
- Go语言的接口interface、struct和组合、继承
Go语言的interface概念相对于C++中的基类,通过interface来实现多态功能. 在C++中,当需要实现多态功能时,步骤是首先定义一个基类,该基类使用虚函数或者纯虚函数抽象了所有子类会用到 ...
- Go语言的接口与反射
美女图片没啥用,就是为了好看 本文还在完善中... go总体而言是一门比较好入门的语言,许多特性都很精简易懂,但是接口与反射除外.他们真的让人头疼,不知道是自身资质问题还是怎么着,总是觉得很多书上写的 ...
随机推荐
- PDOStatement::fetch
PDOStatement::fetch — 从结果集中获取下一行(PHP 5 >= 5.1.0, PECL pdo >= 0.1.0) 说明 语法 mixed PDOStatement:: ...
- Python PIL方式打开的图片判断维度
1. PIL方式打开的图片判断维度 好久没更新啦,哈哈哈~~!今天跟宝宝们分享一篇如何判断灰度图像和彩色图像维度的方法.我们在读取灰度图像和彩色图像时,发现读取出来的图片维度不同,当我们要做后续 ...
- Pr剪辑
目录 Pr剪辑教程 入门基础 创建序列类别 处理非正常序列 导出文件 导出设置 导入各类别素材 简单使用: 剪辑素材常用方法 剃刀工具 选择工具 波纹编辑工具 打入点和出点 剪辑速度 整个素材视频速度 ...
- 关于ORACLE索引的几种扫描方式
------------恢复内容开始------------ ------------恢复内容开始------------ 一条sql执行的效率因执行计划的差异而影响,经常说这条sql走索引了,那条s ...
- CF习题集一
CF习题集一 一.CF915E Physical Education Lessons 题目描述 \(Alex\)高中毕业了,他现在是大学新生.虽然他学习编程,但他还是要上体育课,这对他来说完全是一个意 ...
- three.js 着色器材质基础(一)
说起three.js,着色器材质总是绕不过的话题,今天郭先生就说一说什么是着色器材质.着色器材质是很需要灵感和数学知识的,可以用简短的代码和绘制出十分丰富的图像,可以说着色器材质是脱离three.js ...
- 2020-04-22:谈谈JDK1.8下的HashMap在并发情况下链表成环的过程。(挖)
福哥答案2020-04-22: jdk1.8下的hashmap采用的是尾插法,不会有链表成环的问题.jdk1.7下采用的头插***有链表成环的问题. hashmap成环原因的代码出现在transfer ...
- 用它5分钟以后,我放弃用了四年的 Flask
有一个非常简单的需求:编写一个 HTTP接口,使用 POST 方式发送一个 JSON 字符串,接口里面读取发送上来的参数,对其中某个参数进行处理,并返回. 如果我们使用 Flask 来开发这个接口,那 ...
- Golang并发编程基础
硬件 内存 作为并发编程一个基础硬件知识储备,首先要说的就是内存了,总的来说在绝大多数情况下把内存的并发增删改查模型搞清楚了其他的基本上也是异曲同工之妙. 内存芯片--即我们所知道的内存颗粒,是一堆M ...
- JavaFX桌面应用-视频转码工具(支持爱奇艺qsv转mp4)
最近由于需要将在爱奇艺下载的视频(qsv)转化了mp4,用JavaFX开发一个视频转码工具,算是JavaFX开发的第一个应用吧. 支持qsv转码mp4,理论上支持各种格式,仅测试了flv,qsv格式. ...