本文来自: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 接口使用的更多相关文章

  1. App开发:模拟服务器数据接口 - MockApi

    为了方便app开发过程中,不受服务器接口的限制,便于客户端功能的快速测试,可以在客户端实现一个模拟服务器数据接口的MockApi模块.本篇文章就尝试为使用gradle的android项目设计实现Moc ...

  2. 干货来袭-整套完整安全的API接口解决方案

    在各种手机APP泛滥的现在,背后都有同样泛滥的API接口在支撑,其中鱼龙混杂,直接裸奔的WEB API大量存在,安全性令人堪优 在以前WEB API概念没有很普及的时候,都采用自已定义的接口和结构,对 ...

  3. 12306官方火车票Api接口

    2017,现在已进入春运期间,真的是一票难求,深有体会.各种购票抢票软件应运而生,也有购买加速包提高抢票几率,可以理解为变相的黄牛.对于技术人员,虽然写一个抢票软件还是比较难的,但是还是简单看看123 ...

  4. Java基础Map接口+Collections工具类

    1.Map中我们主要讲两个接口 HashMap  与   LinkedHashMap (1)其中LinkedHashMap是有序的  怎么存怎么取出来 我们讲一下Map的增删改查功能: /* * Ma ...

  5. Java基础Map接口+Collections

    1.Map中我们主要讲两个接口 HashMap  与   LinkedHashMap (1)其中LinkedHashMap是有序的  怎么存怎么取出来 我们讲一下Map的增删改查功能: /* * Ma ...

  6. java基础_集合List与Set接口

    List接口继承了Collection的方法  当然也有自己特有的方法向指定位置添加元素   add(索引,添加的元素); 移除指定索引的元素   remove(索引) 修改指定索引的元素   set ...

  7. 【WCF】自定义错误处理(IErrorHandler接口的用法)

    当被调用的服务操作发生异常时,可以直接把异常的原始内容传回给客户端.在WCF中,服务器传回客户端的异常,通常会使用 FaultException,该异常由这么几个东东组成: 1.Action:在服务调 ...

  8. PHP以接口方式实现多重继承(完全模拟)--学习笔记

     1.UML类图: 2.PHP代码: <?php /** * Created by PhpStorm. * User: andy * Date: 16-11-23 * Time: 下午7:57 ...

  9. 【微框架】Maven +SpringBoot 集成 阿里大鱼 短信接口详解与Demo

    Maven+springboot+阿里大于短信验证服务 纠结点:Maven库没有sdk,需要解决 Maven打包找不到相关类,需要解决 ps:最近好久没有写点东西了,项目太紧,今天来一篇 一.本文简介 ...

  10. 【接口开发】浅谈 SOAP Webserver 与 Restful Webserver 区别

    接口,强大,简单,交互,跨越平台 下面简单阐述这两大接口思想 一 REST: REST是一种架构风格,其核心是面向资源,REST专门针对网络应用设计和开发方式,以降低开发的复杂性,提高系统的可伸缩性. ...

随机推荐

  1. 阶段5 3.微服务项目【学成在线】_day18 用户授权_17-细粒度授权-获取当前用户信息

    3.4.1需求分析 要想实现只查询自己的课程信息则需要获取当前用户所属的企业id. 1.认证服务在用户认证通过将用户所属公司id等信息存储到jwt令牌中. 2.用户请求到达资源服务后,资源服务需要取出 ...

  2. zabbix4.4 (server,proxy,agent)安装部署实战

    一. 部署架构图 二. 安装环境及版本信息 操作系统:centos7 zabbix版本: 4.4 mysql: 5.7.28 三.zabbix server安装(192.168.182.132) rp ...

  3. C#读取带命名空间的xml

    首先带有命名空间的xml读取可以使用Xml.Linq,也可以使用xpath,本文将采用xpath的方式解析. 原文参考了:https://www.cnblogs.com/duanjt/p/544054 ...

  4. SQLite 的简单使用

    程序运行结果如下 : 输入name和age,点击"确认插入"按钮,即插入一条记录到数据库. 程序代码如下:(有详细注释,相信大家都看得懂哈!!!) ////  ViewContro ...

  5. iOS技术面试06:应用程序

    1.NSRunLoop的实现机制,及在多线程中如何使用 NSRunLoop是IOS消息机制的处理模式 >1.NSRunLoop的主要作用:控制NSRunLoop里面线程的执行和休眠,在有事情做的 ...

  6. 【c# 学习笔记】索引器

    当一个类包含数组成员时,索引器 的使用将大大地简化对类中数组成员的访问.索引器的定义类似于属性,也具有GET访问器和set访问器,如下: [修饰符] 数据类型 this[索引类型 index] { g ...

  7. 51.Qt-使用ajax获取ashx接口的post数据

    由于当前C++项目需要使用ajax库去post调用ashx接口,接口地址如下所示: 需要传递的参数如下: 然后发现qml比较好调用ajax.js库,所以本章通过C++界面去获取qml方法来实现调用as ...

  8. vue中的$listeners属性作用

    一.当组件的根元素不具备一些DOM事件,但是根元素内部元素具备相对应的DOM事件,那么可以使用$listeners获取父组件传递进来的所有事件函数,再通过v-on="xxxx"绑定 ...

  9. S03_CH05_AXI_DMA_HDMI图像输出

    S03_CH05_AXI_DMA_HDMI图像输出 5.1概述 本课程是在前面课程基础上添加HDMI IP 实现HDMI视频图像的输出.本课程出了多了HDMI输出接口,其他内容和<S03_CH0 ...

  10. Mybatis-Plus myBatis的增强工具

    1. Mybatis-Plus简介 1.1. 什么是Mybatis-Plus MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为 ...