go接口详解
go面向接口编程知识点
接口定义与格式
接口(interface)是一种类型,用来定义行为(方法)。这句话有两个重点,类型和定义行为。
首先解释定义行为:
接口即一组方法定义的集合,定义了对象的一组行为,就是定义了一些函数,由具体的类型实例实现具体的方法。
换句话说,一个接口就是定义(规范或约束),接口并不会实现这些方法,具体的实现由类实现,实现接口的类必须严格按照接口的声明来实现接口提供的所有功能。接口的作用应该是将定义与实现分离,降低耦合度。
在多人合作开发同一个项目时,接口表示调用者和设计者的一种约定,事先定义好相互调用的接口可以大大提高开发的效率。有了接口,就可以在不影响现有接口声明的情况下,修改接口的内部实现,从而使兼容性问题最小化。
接口的定义格式:
type Namer interface {
Method1(param_list) return_type //方法名(参数列表) 返回值列表
Method2(param_list) return_type //方法名(参数列表) 返回值列表
......
}
隐式实现及实现条件
怎么实现接口:
实现接口的类并不需要显式声明,只需要实现接口所有的函数就表示实现了该接口,而且类还可以拥有自己的方法。
接口能被哪些类型实现:
接口可以被结构体实现,也可以被函数类型实现。
接口被实现的条件:
接口被实现的条件一:接口的方法与实现接口的类型方法格式一致(方法名、参数类型、返回值类型一致)。
接口被实现的条件二:接口中所有方法均被实现。
package main import "fmt" type Shaper interface {
Area() float64
// Perimeter() float64
} type Rectangle struct {
length float64
width float64
} // 实现 Shaper 接口中的方法
func (r *Rectangle) Area() float64 {
return r.length * r.width
} // Set 是属于 Rectangle 自己的方法
func (r *Rectangle) Set(l float64, w float64) {
r.length = l
r.width = w
} func main() {
rect := new(Rectangle) //创建指针类型的结构体实例(类实例)
rect.Set(2, 3)
areaIntf := Shaper(rect) //这里将指针类型实例赋值给接口,下面会介绍。
fmt.Printf("The rect has area: %f\n", areaIntf.Area())
}
接口赋值
现在来解释接口是一个类型,本质是一个指针类型,那么什么样的值可以赋值给接口,有两种:实现了该接口的类或者接口。
1.将对象赋值给接口
当接口实例中保存了自定义类型的实例后,就可以直接从接口上调用它所保存的实例的方法。
package main import (
"fmt"
) //定义接口
type Testinterface interface{
Teststring() string
Testint() int
} //定义结构体
type TestMethod struct{
name string
age int
} //结构体的两个方法隐式实现接口
func (t *TestMethod)Teststring() string{
return t.name
} func (t *TestMethod)Testint() int{
return t.age
} func main(){
T1 := &TestMethod{"ling",34}
T2 := TestMethod{"gos",43}
//接口本质是一种类型
//接口赋值:只要类实现了该接口的所有方法,即可将该类赋值给这个接口
var Test1 Testinterface //接口只能是值类型
Test1 = T1 //TestMethod类的指针类型实例传给接口
fmt.Println(Test1.Teststring())
fmt.Println(Test1.Testint()) Test2 := T2 //TestMethod类的值类型实例传给接口
fmt.Println(Test2.Teststring())
fmt.Println(Test2.Testint())
}
2.将接口赋值给另一个接口
1.只要两个接口拥有相同的方法列表(与次序无关),即是两个相同的接口,可以相互赋值
2.接口赋值只需要接口A的方法列表是接口B的子集(即假设接口A中定义的所有方法,都在接口B中有定义),那么B接口的实例可以赋值给A的对象。反之不成立,即子接口B包含了父接口A,因此可以将子接口的实例赋值给父接口。
3.即子接口实例实现了子接口的所有方法,而父接口的方法列表是子接口的子集,则子接口实例自然实现了父接口的所有方法,因此可以将子接口实例赋值给父接口。
3.接口类型作为参数
第一点已经说了可以将实现接口的类赋值给接口,而将接口类型作为参数很常见。这时,那些实现接口的实例都能作为接口类型参数传递给函数/方法。
package main
import (
"fmt"
)
//Shaper接口
type Shaper interface {
Area() float64
}
// Circle struct结构体
type Circle struct {
radius float64
}
// Circle类型实现Shaper中的方法Area()
func (c *Circle) Area() float64 {
return 3.14 * c.radius * c.radius
} func main() {
// Circle的指针类型实例
c1 := new(Circle)
c1.radius = 2.5
//将 Circle的指针类型实例c1传给函数myArea,接收类型为Shaper接口
myArea(c1)
}
func myArea(n Shaper) {
fmt.Println(n.Area())
}
空接口
空接口是指没有定义任何接口方法的接口。没有定义任何接口方法,意味着Go中的任意对象都已经实现空接口(因为没方法需要实现),只要实现接口的对象都可以被接口保存,所以任意对象都可以保存到空接口实例变量中。
空接口的定义方式:
type empty_int interface {}
更常见的,会直接使用interface{}
作为一种类型,表示空接口。例如:
// 声明一个空接口实例
var i interface{}
再比如函数使用空接口类型参数:
func myfunc(i interface{})
如何使用空接口
可以定义一个空接口类型的array、slice、map、struct等,这样它们就可以用来存放任意类型的对象,因为任意类型都实现了空接口。
package main import "fmt" func main() {
any := make([]interface{}, 5)
any[0] = 11
any[1] = "hello world"
any[2] = []int{11, 22, 33, 44}
for _, value := range any {
fmt.Println(value)
}
}
11
hello world
[11 22 33 44]
<nil>
<nil>
通过空接口类型,Go也能像其它动态语言一样,在数据结构中存储任意类型的数据。
接口嵌套
接口可以嵌套,嵌套的内部接口将属于外部接口,内部接口的方法也将属于外部接口。
另外在类型嵌套时,如果内部类型实现了接口,那么外部类型也会自动实现接口,因为内部属性是属于外部属性的。
type ReadWrite interface {
Read(b Buffer) bool
Write(b Buffer) bool
} type Lock interface {
Lock()
Unlock()
} type File interface {
//ReadWrite为内部接口
ReadWrite
//Lock为内部接口
Lock
Close()
}
类型断言
类型断言为判断一个类型有没有实现接口。
假如我现在写了一个结构体类型 MyFile
来实现上面的 File
接口,那么我如何知道 MyFile
是否实现了 File
接口呢?
package main import "fmt" type MyFile struct{} func (m *MyFile) Read() bool {
fmt.Printf("Read()\n")
return true
} // ...
// 假设我这里相继实现了 Write(), Lock(),Unlock() 和 Close() 方法 func main() {
my := new(MyFile)
fIntf := File(my) // 看这里,看这里
if v, ok := fIntf.(*MyFile); ok {
v.Read()
}
}
类型断言的格式:
if v, ok : = varI.(T) ; ok {
// checked type assertion
//do something
return
}
如果 v
是 varI
转换到类型 T
的值,ok
会是 true
;否则 v
是类型 T
的零值,ok
是 false
。
要是多个类型实现了同一个接口,比如前面的 areaIntf
,要如何测试呢?
那就要用 type-switch
来判断了。
switch t := areaIntf.(type) {
case *Rectangle:
// do something
case *Triangle:
// do something
default:
// do something
}
多态
1、多个类型(结构体)可以实现同一个接口。
2、一个类型(结构体)可以实现多个接口。
3、实现接口的类(结构体)可以赋值给接口。
package main import "fmt" type Shaper interface {
Area() float64
} // ==== Rectangle ====
type Rectangle struct {
length float64
width float64
} // 实现 Shaper 接口中的方法
func (r *Rectangle) Area() float64 {
return r.length * r.width
} // Set 是属于 Rectangle 自己的方法
func (r *Rectangle) Set(l float64, w float64) {
r.length = l
r.width = w
} // ==== Triangle ====
type Triangle struct {
bottom float64
hight float64
} func (t *Triangle) Area() float64 {
return t.bottom * t.hight / 2
} func (t *Triangle) Set(b float64, h float64) {
t.bottom = b
t.hight = h
} // ==== Triangle End ==== func main() {
rect := new(Rectangle)
rect.Set(2, 3)
areaIntf := Shaper(rect) //这种方法只能将指针类型的类示例赋值给接口
fmt.Printf("The rect has area: %f\n", areaIntf.Area()) triangle := new(Triangle)
triangle.Set(2, 3)
areaIntf = Shaper(triangle) //这种方法只能将指针类型的类示例赋值给接口
fmt.Printf("The triangle has area: %f\n", areaIntf.Area())
}
go接口详解的更多相关文章
- JDBC常用接口详解
JDBC中常用接口详解 ***DriverManager 第一.注册驱动 第一种方式:DriverManager.registerDriver(new com.mysql.jdbc.Driver()) ...
- Java6.0中Comparable接口与Comparator接口详解
Java6.0中Comparable接口与Comparator接口详解 说到现在,读者应该对Comparable接口有了大概的了解,但是为什么又要有一个Comparator接口呢?难道Java的开发者 ...
- socket接口详解
1. socket概述 socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信. socket起源于UNIX,在Unix一切 ...
- “全栈2019”Java第八十四章:接口中嵌套接口详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第八十三章:内部类与接口详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- Java接口 详解(二)
上一篇Java接口 详解(一)讲到了接口的基本概念.接口的使用和接口的实际应用(标准定义).我们接着来讲. 一.接口的应用—工厂设计模式(Factory) 我们先看一个范例: package com. ...
- [转载]MII/MDIO接口详解
原文地址:MII/MDIO接口详解作者:心田麦浪 本文主要分析MII/RMII/SMII,以及GMII/RGMII/SGMII接口的信号定义,及相关知识,同时本文也对RJ-45接口进行了总结,分析了在 ...
- map接口详解
1.Map接口详解(1)映射(map)是一个存储键.键值对的对象,给定一个键,可以查询得到它的值,键和值都可以是对象(2)键必须是唯一的,值可以重复(Map接口映射唯一的键到值)(3)有些映射可以接收 ...
- ReadWriteLock 接口详解
ReadWriteLock 接口详解 这是本人阅读ReadWriteLock接口源码的注释后,写出的一篇知识分享博客 读写锁的成分是什么? 读锁 Lock readLock(); 只要没有写锁,读锁可 ...
- java.io.DataInput接口和java.io.DataOutput接口详解
public interface DataInput DataInput 接口用于从二进制流中读取字节,并重构所有 Java 基本类型数据.同时还提供根据 UTF-8 修改版格式的数据重构 Strin ...
随机推荐
- 简单了解linux内核
linux内核是单块结构Linux能动态的按需装载或卸载模块Linux内核线程以一种十分受限制的方式来周期性地执行几个内核函数,因为linux内核线程不能执行用户程序,因此,她们并不代表基本的可执行上 ...
- mysql-5.7.9-winx64遇坑记
昨天在mysql5.0上导入sql文件时,一直卡在一个地方报错,也没仔细分析,认为应该是mysql版本太低不支持这个语法而已.遂决心下载一个最新版本的mysql,却浑然不知前面无数的坑已经埋伏好了在等 ...
- c++中减字符0的作用(转)
在刷OJ题的时候遇到要读取“2013-3-1”形式的日期然后计算这个日期是该年的第几天, 显然我们读取的是字符串,但是计算第几天却要整型数来计算,这是这个问题的难点,下面是解决这个问题的代码: int ...
- 小白学 Python 爬虫(40):爬虫框架 Scrapy 入门基础(七)对接 Selenium 实战
人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...
- JavaScript面向对象 实例与原型
JavaScript 面向对象 和 C# 不太一样,js 的对象是继承自原型的如下: 首先创建一个 js 实例 new function function f () {} 这个函数 会继承 Func ...
- 【C&数据结构】---关于链表结构的前序插入和后序插入
刷LeetCode题目,需要用到链表的知识,忽然发现自己对于链表的插入已经忘得差不多了,以前总觉得理解了记住了,但是发现真的好记性不如烂笔头,每一次得学习没有总结输出,基本等于没有学习.连复盘得机会都 ...
- 2019 牛客国庆集训day1 2019 点分治
题目链接:https://ac.nowcoder.com/acm/contest/1099/I 点分治,计算路径数的时候,先将每个点到根的距离模2019,计算的时候就可以O(n)求出数目,对于模201 ...
- MySql查看修改l时区
# 查看时区 show variables like '%time_zone%'; # 设置全局 set global time_zone='+8:00'; # 设置当前会话 set time_zo ...
- Spring(三)核心容器 - ApplicationContext 上下文启动准备
目录 前言 正文 第一步:prepareRefresh 第二步:obtainFreshBeanFactory 第三步:prepareBeanFactory 第四步:postProcessBeanFac ...
- 解决android sdk无法更新 更新慢的问题
使用不同平台开发android应用的时候都要先搭建开发环境. 这里介绍一下搭建开发环境过程中更新和下载android sdk的一种方法: 第一步:配置android sdk manager的代理服务, ...