Go 接口使用
本文来自:CSDN博客
感谢作者:fengfengdiandia
查看原文:go 接口
Go 语言不是一种 “传统” 的面向对象编程语言:它里面没有类和继承的概念。
但是 Go 语言里有非常灵活的接口概念,通过它可以实现很多面向对象的特性。
接口 定义了一个 方法的集合,但是这些方法 不包含实现代码,它们是 抽象的,接口里也 不能包含变量。
定义格式
定义接口的一般格式:
type Namer interface {
Method1(param_list) return_type
Method2(param_list) return_type
...
}
上面的 Namer 就是一个接口类型。
在 Go 语言中 接口 可以有值, 一个 接口类型 的变量或一个 接口值:var ai Namer ,ai 是一个 multiword 数据结构,它的值是 nil。
它本质上是一个 指针,虽然不完全是一回事。指向接口值的指针是非法的,会导致代码错误。
类型(比如结构体)实现接口方法集中的方法,实现了 Namer 接口类型的变量可以赋值给 ai,此时方法表中的指针会指向被实现的接口方法。
实现某个接口的类型,除了实现接口的方法外,还可以有自己的方法。
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())
}
如果去掉 Shaper 中 Perimeter() float64 的注释,编译的时候会遇到下面的错误,这是因为 Rectangle 没有实现 Perimeter() 方法。
cannot convert rect (type *Rectangle) to type Shaper: *Rectangle does not implement Shaper (missing Perimeter method)
多态
1、多个类型可以实现同一个接口。
2、一个类型可以实现多个接口。
下面我们增加一个类型 Triangle,同样也为它实现 Shaper 接口。
package main
import "fmt"
type Shaper interface {
Area() float64
// Perimeter() 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
}
// ==== Rectangle End ====
// ==== 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())
}
灵活性
接口的定义是比较灵活的。
假设接口和类型处于不同的包中,只要类型实现了接口中的全部方法,那么它就实现了此接口。
现在我们将 Shaper 的定义放在 shaper 包 下, Rectangle 和 Triangle 的定义放在 test 包 下:
[root@ src]# tree
├── test
│ └── test.go
├── main.go
└── shaper
└── shaper.go
shaper.go
package shaper
type Shaper interface {
Area() float64
}
test.go
package test
// ==== 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
}
// ==== Rectangle End ====
// ==== 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 ====
// main.go
package main
import (
"fmt"
"shaper"
"test"
)
func main() {
rect := new(test.Rectangle)
rect.Set(2, 3)
areaIntf := shaper.Shaper(rect)
fmt.Printf("The rect has area: %f\n", areaIntf.Area())
triangle := new(test.Triangle)
triangle.Set(2, 3)
areaIntf = shaper.Shaper(triangle)
fmt.Printf("The triangle has area: %f\n", areaIntf.Area())
}
现在运行 main.go 看看结果吧,嗯嗯,没什么问题,^_^
The rect has area: 6.000000
The triangle has area: 3.000000
接口嵌套
一个接口可以包含一个或多个其他的接口,这相当于直接将这些内嵌接口的方法列举在外层接口中一样。
比如接口 File 包含了 ReadWrite 和 Lock 的所有方法,它还额外有一个 Close() 方法。
type ReadWrite interface {
Read(b Buffer) bool
Write(b Buffer) bool
}
type Lock interface {
Lock()
Unlock()
}
type File interface {
ReadWrite
Lock
Close()
}
类型断言
假如我现在写了一个结构体类型 MyFile 来实现上面的 File 接口,那么我如何知道 MyFile 是否实现了 File 接口呢?
通常我们使用 类型断言 来测试在某个时刻 varI 是否包含类型 T 的值:
if v, ok : = varI.(T) ; ok { // checked type assertion
Process(v)
return
}
// varI is not of type T
如果 v 是 varI 转换到类型 T 的值,ok 会是 true;否则 v 是类型 T 的零值,ok 是 false。
// main.go
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()
}
}
输出结果是:Read()
要是多个类型实现了同一个接口,比如前面的 areaIntf,要如何测试呢?
那就要用 type-switch 来判断了。
type-switch 类型判断
switch t := areaIntf.(type) {
case *Rectangle:
// do something
case *Triangle:
// do something
default:
// do something
}
Go 接口使用的更多相关文章
- App开发:模拟服务器数据接口 - MockApi
为了方便app开发过程中,不受服务器接口的限制,便于客户端功能的快速测试,可以在客户端实现一个模拟服务器数据接口的MockApi模块.本篇文章就尝试为使用gradle的android项目设计实现Moc ...
- 干货来袭-整套完整安全的API接口解决方案
在各种手机APP泛滥的现在,背后都有同样泛滥的API接口在支撑,其中鱼龙混杂,直接裸奔的WEB API大量存在,安全性令人堪优 在以前WEB API概念没有很普及的时候,都采用自已定义的接口和结构,对 ...
- 12306官方火车票Api接口
2017,现在已进入春运期间,真的是一票难求,深有体会.各种购票抢票软件应运而生,也有购买加速包提高抢票几率,可以理解为变相的黄牛.对于技术人员,虽然写一个抢票软件还是比较难的,但是还是简单看看123 ...
- Java基础Map接口+Collections工具类
1.Map中我们主要讲两个接口 HashMap 与 LinkedHashMap (1)其中LinkedHashMap是有序的 怎么存怎么取出来 我们讲一下Map的增删改查功能: /* * Ma ...
- Java基础Map接口+Collections
1.Map中我们主要讲两个接口 HashMap 与 LinkedHashMap (1)其中LinkedHashMap是有序的 怎么存怎么取出来 我们讲一下Map的增删改查功能: /* * Ma ...
- java基础_集合List与Set接口
List接口继承了Collection的方法 当然也有自己特有的方法向指定位置添加元素 add(索引,添加的元素); 移除指定索引的元素 remove(索引) 修改指定索引的元素 set ...
- 【WCF】自定义错误处理(IErrorHandler接口的用法)
当被调用的服务操作发生异常时,可以直接把异常的原始内容传回给客户端.在WCF中,服务器传回客户端的异常,通常会使用 FaultException,该异常由这么几个东东组成: 1.Action:在服务调 ...
- PHP以接口方式实现多重继承(完全模拟)--学习笔记
1.UML类图: 2.PHP代码: <?php /** * Created by PhpStorm. * User: andy * Date: 16-11-23 * Time: 下午7:57 ...
- 【微框架】Maven +SpringBoot 集成 阿里大鱼 短信接口详解与Demo
Maven+springboot+阿里大于短信验证服务 纠结点:Maven库没有sdk,需要解决 Maven打包找不到相关类,需要解决 ps:最近好久没有写点东西了,项目太紧,今天来一篇 一.本文简介 ...
- 【接口开发】浅谈 SOAP Webserver 与 Restful Webserver 区别
接口,强大,简单,交互,跨越平台 下面简单阐述这两大接口思想 一 REST: REST是一种架构风格,其核心是面向资源,REST专门针对网络应用设计和开发方式,以降低开发的复杂性,提高系统的可伸缩性. ...
随机推荐
- JS微信网页使用图片预览(放大缩小)
前言 需求在微信网页中客户点击图片可进行预览放大缩小功能,网上找了各种js方式实现, 唯一的麻烦就是不兼容或者和项目框架不兼容 次函数只只用于部分客户端,否则会出现 WeixinJSBridge is ...
- java精确计算工具类
java精确计算工具类 import java.math.BigDecimal; import java.math.RoundingMode; import java.math.BigDecimal; ...
- C# winform中使用Panel调节窗口变化是各控件的位置(转)
我的目的是在窗口上有些控件,在窗口大小变化时,上面的控件位置不动,大小也不动.下面的控件随着窗口的大小变化而变大. 做法是用两个panel,panelTop和panelFill.上面的控件都放到pan ...
- ubuntu18.04下eclipse修改maven源为阿里源
下载安装Java和Eclipse:https://www.cnblogs.com/zifeiy/p/9030111.html 然后命令行安装Maven(不是必须的): sudo apt-get ins ...
- 动态绑定easyui datagrid列名
根据实时数据在同一个DataGrid中显示不同字段,本身easyui并没有支持动态绑定列名,只有show属性显示或隐藏某字段.今天在网上看到直接修改easyui类库动态绑定列名的方法,废话不多说直接借 ...
- iOS——使用FMDB进行数据库操作(转载)
iOS 使用FMDB进行数据库操作 https://github.com/ccgus/fmdb [摘要]本文介绍iOS 使用FMDB进行数据库操作,并提供详细的示例代码供参考. FMDB 使用方法 A ...
- 01.轮播图之五 :一个 imageView也能 作 轮播
这个是最近才写的,本以为实现起来很有难度,需要更高深的理论, 写完之后,才发现自己错误的离谱: 之所以能用一个imageview 实现轮播 基于两点::: 使用 imageview 的layer 层设 ...
- Docker 持久存储介绍(十三)
目录 一.Docker 数据存储 二.Bind mount 1.详细介绍 2.如何使用 -v or --volume 语法 --mount 语法 两者区别 3.使用场景 4.使用案例 存在目录 bin ...
- 泰乐事(Telos)白皮书中文版 <零> 封面及目录
<泰乐事白皮书> 一个可持续发展的去中心化EOSIO网络 作者:道格拉斯·合恩 泰乐事(Telos)—— 事物的终极目标. Telos一词来源于希腊语ΤΈΛΟΣ. “一颗橡果的终极目标是成 ...
- C语言 班级档案管理系统实现
代码地址:github地址 班级档案管理系统 原题目要求是对一个有N个学生的班级,通过该系统实现对该班级学生的基本信息进行录入. 显示.修改.删除.保存等操作的管理. 由于个人需要,我单独将项目改造为 ...