本文来自: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. js文件中如何使用 获取EL表达式的值

    转: js文件中如何使用 获取EL表达式的值 原先做法是在jsp页面引入头文件 <%@ page language="java" pageEncoding="UTF ...

  2. Qt编写控件属性设计器4-加载属性

    一.前言 控件能加载拖曳拉伸了,这些都是基本的前提工作,接下来的重点就是要动态加载选中控件的属性了,Qt的属性机制那是异常的强大,只能用强大到爆来形容,Qt中编写自定义控件,如果属性都用Q_PROPE ...

  3. python 运算符和小数据池

    计算机可以进行的运算有很多种,可不只加减乘除这么简单,运算按种类可分为算数运算.比较运算.逻辑运算.赋值运算.成员运算.身份运算.位运算,今天我们暂只学习算数运算.比较运算.逻辑运算.赋值运算 算数运 ...

  4. beyond compare 4 的试用期过了的处理办法

    beyond compare 是一款好用的对比软件,在广大码农开发过程中,占有很重要的地位,特别是在需要经常合并版本(都是泪) beyond compare 4  30天试用期过期了,在网上找的密钥也 ...

  5. tk mybatis动态sql中过滤不使用的字段

    实体字段如下 @Data @NoArgsConstructor @AllArgsConstructor @Builder /*** * app图标 */ @JsonFormat public clas ...

  6. delphi ADOQUery中错误解决方法"无法为更新定位行。一些值可能已在最后...

    使用delphi中的ADOQuery控件中自带的,insert ,edit,delete此操作时,有时会出现下面的错误提示,提示错误信息:"无法为更新定位行.一些值可能已在最后一次读取后已更 ...

  7. weblogic12.1.3安装

    weblogic weblogic12.1.3安装 环境: centos7.5 ip: 192.168.0.94 1.安装jdk 2.安装 weblogic 下载.解压安装包 wls1213_dev. ...

  8. Mysql安装、查看密码、修改密码、初始化、修改字符类型

    安装mysql 参照python篇一键安装lnmp.安装完之后再按照下面修改密码,修改配置文件,否则安装的时候就修改配置文件会出错. 注意:这也是二进制安装mysql.另一种二进制安装容易出错,生产环 ...

  9. JMETER安装教程

    jmeter的安装教程 1:安装jdk并且配置好环境变量,此处就不做赘述(前面的文档中有) 2:下载jmeter文件和jmeter的插件文件 JMeter:http://jmeter.apache.o ...

  10. iOS技术面试02:内存管理

    怎么保证多人开发进行内存泄露的检查. 如何定位内存泄露? 1> 使用Analyze进行代码的静态分析(检测有无潜在的内存泄露) 2> 通过leak检查在程序运行过程中有无内存泄露 3> ...