[golang note] 接口使用
侵入式接口
√ 在其他一些编程语言中,接口主要是作为不同组件之间的契约存在,即规定双方交互的规约。
√ 对契约的实现是强制的,即必须确保用户的确实现了该接口,而实现一个接口,需要从该接口继承。
√ 如果一个类实现了接口A,即便另一个接口B与A的方法列表相同,甚至连接口名都相同,但位于不同的命名空间下,那么编译器并不认为这两个接口是一样的。
√ 所谓侵入的主要表现在于实现继承接口的类需要明确声明自己实现自某个接口。
√ 侵入式接口常纠结的问题是:应该提供哪些接口好呢?如果两个类实现了相同的接口,应该把接口放到哪个包好呢?
• c++接口示例
#include <string>
#include <iostream>
using namespace std; class IPerson
{
public:
IPerson() {}
virtual ~IPerson() {} public:
virtual void SetName(const string &name) = ;
virtual const string &GetName() = ;
virtual void Work() = ;
}; class Teacher : public IPerson
{
private:
string m_Name; public:
Teacher() : m_Name("") {}
virtual ~Teacher() {} public:
virtual void SetName(const string &name)
{
m_Name = name;
} virtual const string &GetName()
{
return m_Name;
} virtual void Work()
{
cout << "I'm a teacher" << endl;
}
}; int main()
{
IPerson *p = ::new Teacher;
p->SetName("chris");
p->Work();
cout << p->GetName() << endl;
delete p;
return ;
}
• java接口示例
非侵入式接口
√ 在golang中,一个类只需要实现了某个接口中的所有函数,我们就说这个类实现了该接口。
√ 在golang中,一个类不需要继承于接口,也不需要知道有哪些接口的存在。
√ 在golang中,接口和类之间不再有所谓的契约关系,因此实现类的时候,只需要关心自己应该提供哪些方法,不用再纠结接口需要拆得多细才合理。接口由使用方按需定义,而不用事前规划,也不需要绘制类库的继承树图。
√ 在golang中,不用为了实现一个接口而导入一个包,因为多引用一个外部的包,就意味着更多的耦合。接口由使用方按自身需求来定义,使用方无需关心是否有其他模块定义过类似的接口。
• 接口赋值
▶ 对象实例赋值给接口
▪ 语法如下
// 接口定义
type IMyInterface1 interface {
Func1(参数列表) (返回值列表)
Func2(参数列表) (返回值列表)
Func3(参数列表) (返回值列表)
} type IMyInterface2 interface {
Func1(参数列表) (返回值列表)
} // 类定义
type MyClass struct {
// 成员变量
} func (p *MyClass) Func1(参数列表) (返回值列表) {
// 函数体
} func (p *MyClass) Func2(参数列表) (返回值列表) {
// 函数体
} func (p *MyClass) Func3(参数列表) (返回值列表) {
// 函数体
} func (p *MyClass) Func4(参数列表) (返回值列表) {
// 函数体
} // 接口赋值
var myInterface1 IMyInterface1 = new(MyClass)
var myInterface2 IMyInterface2 = new(MyClass) // 接口调用
myInterface1.Func1(参数列表)
myInterface1.Func2(参数列表)
myInterface1.Func3(参数列表) myInterface2.Func1(参数列表)
▪ 示例如下
package main
import "fmt"
type IMyInterface1 interface {
Func1() bool
Func2() bool
Func3() bool
}
type IMyInterface2 interface {
Func1() bool
}
type MyClass struct {
}
func (p *MyClass) Func1() bool {
fmt.Println("MyClass.Func1()")
return true
}
func (p *MyClass) Func2() bool {
fmt.Println("MyClass.Func2()")
return true
}
func (p *MyClass) Func3() bool {
fmt.Println("MyClass.Func3()")
return true
}
func (p *MyClass) Func4() bool {
fmt.Println("MyClass.Func4()")
return true
}
func main() {
var myInterface1 IMyInterface1 = new(MyClass)
var myInterface2 IMyInterface2 = new(MyClass)
myInterface1.Func1() // MyClass.Func1()
myInterface1.Func2() // MyClass.Func2()
myInterface1.Func3() // MyClass.Func3()
myInterface2.Func1() // MyClass.Func1()
}
▶ 接口之间赋值
√ 在golang中,只要两个接口拥有相同的方法列表,那么它们就是等同的,可以相互赋值。
√ 在golang中,等同的接口可以分布在不同的包中,包并不是判断接口是否等同的条件之一。
√ 在golang中,接口赋值并不要求两个接口必须等价。如果接口A的方法列表是接口B的方法列表的子集,那么接口B是可以赋值给接口A,但是反过来不成立。
▪ 语法如下
// 接口定义
type IMyInterface1 interface {
Func1(参数列表) (返回值列表)
Func2(参数列表) (返回值列表)
} type IMyInterface2 interface {
Func1(参数列表) (返回值列表)
Func2(参数列表) (返回值列表)
} // 类定义
type MyClass struct {
// 成员变量
} func (p *MyClass) Func1(参数列表) (返回值列表) {
// 函数体
} func (p *MyClass) Func2(参数列表) (返回值列表) {
// 函数体
} func (p *MyClass) Func3(参数列表) (返回值列表) {
// 函数体
} // 接口赋值
var myInterface1 IMyInterface1 = new(MyClass)
var myInterface2 IMyInterface2 = myInterface1 // 接口调用
myInterface1.Func1(参数列表)
myInterface1.Func2(参数列表) myInterface2.Func1(参数列表)
myInterface2.Func2(参数列表)
▪ 示例如下
package main
import "fmt"
type IMyInterface1 interface {
Func1() bool
Func2() bool
}
type IMyInterface2 interface {
Func1() bool
Func2() bool
}
type IMyInterface3 interface {
Func1() bool
}
type MyClass struct {
}
func (p *MyClass) Func1() bool {
fmt.Println("MyClass.Func1()")
return true
}
func (p *MyClass) Func2() bool {
fmt.Println("MyClass.Func2()")
return true
}
func (p *MyClass) Func3() bool {
fmt.Println("MyClass.Func3()")
return true
}
func main() {
var myInterface1 IMyInterface1 = new(MyClass)
var myInterface2 IMyInterface2 = myInterface1 // 等同接口
var myInterface3 IMyInterface3 = myInterface2 // 子集接口
myInterface1.Func1() // MyClass.Func1()
myInterface1.Func2() // MyClass.Func2()
myInterface2.Func1() // MyClass.Func1()
myInterface2.Func2() // MyClass.Func2()
myInterface3.Func1() // MyClass.Func1()
}
• 接口查询
▶ 接口查询
√ 接口查询功能一:检查接口A指向的对象实例O是否实现了接口B,如果是则执行特定的代码。
√ 接口查询功能二:检查接口A指向的对象实例O是否是类型T,如果是则执行特定的代码。
√ 接口查询是运行期行为,编译器编译期不能确定。
▪ 语法如下
// 接口定义
type IMyInterface1 interface {
// 函数列表
} type IMyInterface2 interface {
// 函数列表
} // 类定义
type MyClass struct {
// 成员变量
} // 接口赋值
var myInterface1 IMyInterface1 = new(MyClass) // 查询myInterface1指向的对象实例是否实现了IMyInterface2类型接口,如果实现,返回的myInterface2是指向对象的IMyInterface2类型接口实例
if myInterface2, ok := myInterface1.(IMyInterface2); ok {
// 查询成功,myInterface2为IMyInterface2类型接口实例
} // 查询myInterface1指向的对象是否是MyClass类型实例,如果是,返回的myInstance是指向该对象的实例
if myInstance, ok := myInterface1.(*MyClass); ok{
// 查询成功,myInstance为myInterface1指向的对象实例
}
▪ 示例如下
package main import (
"fmt"
) type IMyInterface1 interface {
Func1() bool
Func2() bool
} type IMyInterface2 interface {
Func1() bool
} type MyClass struct {
Width, Height float64
} func (p *MyClass) Func1() bool {
fmt.Println("MyClass.Func1() - Width =", p.Width)
return true
} func (p *MyClass) Func2() bool {
fmt.Println("MyClass.Func2() - Height =", p.Height)
return true
} func main() {
var myInterface1 IMyInterface1 = &MyClass{, } if myInterface2, ok := myInterface1.(IMyInterface2); ok {
myInterface2.Func1() // MyClass.Func1() - Width = 100
} if myInstance, ok := myInterface1.(*MyClass); ok {
myInstance.Func1() // MyClass.Func1() - Width = 100
myInstance.Func2() // MyClass.Func2() - Height = 200
fmt.Println(myInstance.Width) //
fmt.Println(myInstance.Height) //
}
}
▶ 类型查询
▪ 空接口:Any类型
√ golang中空接口interface{}可以指向任何对象实例。
√ golang中空接口interface{}看起来像是可以指向任何对象的Any类型,类似于COM中的IUnknown。
package main import (
"fmt"
"reflect"
) func main() {
var v1 interface{} =
var v2 interface{} = "abc"
var v3 interface{} = &v2
var v4 interface{} = struct{ X int }{}
var v5 interface{} = &struct{ X int }{} fmt.Println(reflect.TypeOf(v1)) // int
fmt.Println(reflect.TypeOf(v2)) // string
fmt.Println(reflect.TypeOf(v3)) // *interface {}
fmt.Println(reflect.TypeOf(v4)) // struct { X int }
fmt.Println(reflect.TypeOf(v5)) // *struct { X int }
}
▪ 类型查询语法
√ golang中使用type switch语句可以查询接口指向对象的真实数据类型。
var v interface{} = obj
switch instance := v.(type) { // 返回的instance是指向接口v指向的对象
case int:
...
case string:
...
...
default:
}
▪ 类型查询示例
package main import (
"fmt"
) type Stringer interface {
String() string
} type MyString struct {
content string
} func (str *MyString) String() string {
return str.content
} func PrintString(args ...interface{}) {
for _, arg := range args {
switch instance := arg.(type) {
case string:
fmt.Println(instance)
default:
if v, ok := arg.(Stringer); ok { // 如果对象具有Stringer接口
fmt.Println(v.String())
} else { // 不能打印的非字符串类型
fmt.Println("unexpected string type")
}
}
}
} func main() {
PrintString("abc") // abc
PrintString(&MyString{"hello"}) // hello
PrintString() // unexpected string type
}
• 接口组合
√ 类似struct的匿名组合,golang也为接口提供了继承机制,即接口也可以匿名组合,只不过接口只包含方法,而不包含任何成员变量。
√ 接口组合与其他高级编程语言中的表现一致,即接口A可以被另一个接口B继承,形成接口继承体系。
▶ 基本语法
type MyInterface1 interface {
// 函数列表
}
type MyInterface2 interface {
MyInterface1
// 函数列表
}
▶ 继承规则
√ 匿名组合的接口不可拥有同名函数,否则将给出错误:duplicate method XXXX
▪ 示例如下
package main import (
"fmt"
) type MyInterface1 interface {
Func1() bool
} type MyInterface2 interface {
//Func1() bool // duplicate method Func1
Func2() bool
} type MyInterface3 interface {
MyInterface1
MyInterface2 //Func1() bool // duplicate method Func1
Func3() bool
} type MyClass struct {
} func (p *MyClass) Func1() bool {
fmt.Println("MyClass.Func1()")
return true
} func (p *MyClass) Func2() bool {
fmt.Println("MyClass.Func2()")
return true
} func (p *MyClass) Func3() bool {
fmt.Println("MyClass.Func3()")
return true
} func main() {
var myInterface3 MyInterface3 = new(MyClass)
myInterface3.Func1() // MyClass.Func1()
myInterface3.Func2() // MyClass.Func2()
myInterface3.Func3() // MyClass.Func3()
}
[golang note] 接口使用的更多相关文章
- golang(08)接口介绍
原文链接 http://www.limerence2017.com/2019/09/12/golang13/#more 接口简介 golang 中接口是常用的数据结构,接口可以实现like的功能.什么 ...
- [golang note] 网络编程 - RPC编程
net包 • 官方文档 http://godoc.golangtc.com/pkg/net/ Package net provides a portable interface for network ...
- Golang之接口(interface)
Golang最重要的接口,,,, package main import ( "fmt" ) //interface类型默认是指针 /* 接口的实现 Golang中的接口,不需要显 ...
- golang中接口interface和struct结构类的分析
再golang中,我们要充分理解interface和struct这两种数据类型.为此,我们需要优先理解type的作用. type是golang语言中定义数据类型的唯一关键字.对于type中的匿名成员和 ...
- [golang note] 类型系统
值和引用 • 值语义和引用语义 值语义和引用语义的差别在于赋值: b = a b.Modify() 如果b的修改不会影响a的值,那么属于值类型,否则属于引用类型. • 值类型和引用类型 √ 引用类型一 ...
- [golang note] 错误处理
错误处理 • 错误处理的标准模式 √ golang错误处理的标准模式:error接口. √ golang函数如果要返回错误,规范上是将error作为多返回值中的最后一个,但这并非是强制要求. ▶ er ...
- [golang note] 内建类型
基础类型 √ golang内建基础类型有布尔类型.整数类型.浮点类型.复数类型.字符串类型.字符类型和错误类型. 复合类型 √ golang支持的复合类型有指针.数组.数组切片.字典.通道.结构体和接 ...
- Golang的接口
当一只鸟走路像鸭子,游泳像鸭子,叫起来也像鸭子,那么我们就认为它就是鸭子. Duck typing 的理念因此比喻得名. Golang 通过 interface 实现 duck typing. Eff ...
- 七、golang中接口、反射
一.接口定义 1.定义 interface类型可以定义一组方法,但是这些不需要实现,并且interface不能包含任何变量 package main import ( "fmt" ...
随机推荐
- linux环境,crontab报错Authentication token is no longer valid; new one required You (aimonitor) are not allowed to access to (crontab) because of pam configuration.
问题描述: 今天同事反应,一个系统上的某些数据没有生成,看了下,怀疑定时任务没有执行,就看下了crontab,发现报了下面的错误: [aimonitor@4A-LF-w08 ~]$ crontab - ...
- Messages: No result defined for action cn.itcast.oa.test.TestAction and result SUCCESS
Struts Problem Report Struts has detected an unhandled exception: Messages: No result defined for ac ...
- 服务端用例设计的思(tao)路!
服务端的测试简单来说就是除了前端以外的的测试. 总的来说可以分为以下两类: 1. WEB或者APP的提供业务逻辑的服务端接口测试 2. 数据库.缓存系统.中间件..jar包依赖.输入输 ...
- 1-0 superset的安装和配置
Superset安装及教程官网(http://airbnb.io/superset/installation.html)讲解的已经够详细的了,本篇以官网教程为蓝本进行说明. 入门 Superset目前 ...
- 查看用户信息:w
w命令有两个用途: (1) 用于查看当前系统负载(2) 用于查看当前登录用户的行为和信息,执行这个命令可以得知当前登入系统的用户有哪些人,以及他们正在执行哪些程序 [root@localhost ~] ...
- cocos2dx-3.x物理引擎Box2D介绍
理引擎 Cocos2d-x引擎内置了两种物理引擎,它们分别是Box2D和Chipmunk,都是非常优秀的2D物理引擎,而且x引擎将它们都内置在SDK中.Box2D使用较为广泛,在这里选择Box2D来进 ...
- 使用Jquery做分页效果
之前写过一个PHP 的分页效果,但是今天小伙伴和我说了一个不适用后台单纯用前段的JS来写分页,整理了一下,代码如下: html: <div id="containet"> ...
- 演示PostgreSQL的详细安装及配置图解
右击文件选择以管理员身份运行 2 开始执行程序的安装 3 设置安装目录 4 设置数据的保存目录 5 设置数据库管理员密码,请牢记此密码. 6 设置端口号,选择默认的端口号即可 7 根据自己选择设置地区 ...
- win7(64)使用vim碰到的奇怪问题
一直使用conemu做控制台使用vim,操作系统win7 64位,一直用的很好. 今天使用gvim打开文件发现c:\program file(x86)\vim\_vimrc不生效,最奇怪的是,采用控制 ...
- linux 学习的一些书单,对了解android 也有大用
要推荐的书,我在<那两年炼就的Android内功修养>这篇文章中有提到,这里再列一下出来: 语言类: <深度探索C++对象模型>,对应的英文版是<Inside C+++ ...