1 接口是什么

Golang中没有像Python、Java拥有类和对象的概念,其封装对象或说明对象是通过接口来实现的。比如谁能够实现什么样的功能,便能够将其抽象化封装。

接口定义了一组方法(抽象方法集,不包括该方法的具体实现细节),注意不能包含变量。


通过如下格式定义Golang接口:

type Namer interface {
Method1(param_list) return_type1
Method2(param_list) return_type1
Method2(param_list) (return_type1, return_type2)
...
}

上面的Namer就是一个接口类型,是一种抽象类型

如果要实现一个接口就需要实现该接口类型定义中的所有方法

2 实现接口

如果一个类型实现了一个接口中要求的所有方法,那么这个类型实现了这个接口。

例如在fmt包下的io.writer接口

package io
// Implementations must not retain p.
type Writer interface {
// write从p向底层数据流写入len(p)个字节的数据
// 返回实际写入的字节数n,且0<=n<=len(p)
Write(p []byte) (n int, err error)
}

fmt.Printffmt.Sprintf用于实现字符串的格式化,前者用于格式化输出至控制台(os.Stdout),后者把结果以string类型返回.

package fmt

// 接收一个io.writer类型形参
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrintf(format, a)
n, err = w.Write(p.buf)
p.free()
return
} // 返回Fprintf调用
func Printf(format string, a ...interface{}) (n int, err error) {
// w 为os.Stdout
return Fprintf(os.Stdout, format, a...)
} func Sprintf(format string, a ...interface{}) string {
// newPrinter申请一个临时对象池,sync.Pool
p := newPrinter()
// 格式匹配及处理输出格式
p.doPrintf(format, a)
// 将缓冲区数据取出并字符串化
s := string(p.buf)
// 清空缓存区
p.free()
return s
}

os.Stdout返回了一个*File类型,该类型实现了io.Writer接口中的write方法,即os.Stdout是一个io.Writer类型

func (f *File) write(b []byte) (n int, err error) {
n, err = f.pfd.Write(b)
runtime.KeepAlive(f)
return n, err
}

实现接口的例子1:

package main

// 统计输入流字节数
type ByteCounter int func (b *ByteCounter) Write (p []byte) (n int, err error){
*b += ByteCounter(len(p))
return len(p), nil
} func main() {
var counter ByteCounter
counter = 0
testStr = "ByteConter"
fmt.Fprintf(&counter, "%s", testStr)
fmt.Print(&counter) // “11”
}

例子 2:

// 文件数据写入接口
type DataWriter interface {
DataWrite(data []byte) (n int, err error)
} type File struct {
fname string
fmode int
path string
} func (file *File) DataWrite(data []byte) (n int, err error) {
fw, err := os.OpenFile(file.path, os.O_RDWR, 6)
if err != nil{
log.Fatalf("%v", err)
return 0, err
}
defer fw.Close()
n, werr := fw.Write(data)
if werr != nil {
log.Fatalf("%v", err)
return 0, werr
}
fmt.Println("write successfully!")
return n, nil
} func main() { var f File
f = File{
fname: "h.txt",
fmode: 6,
path: "./h.txt",
}
}

实现关系在 Go语言中是隐式的。两个类型之间的实现关系不需要在代码中显式地表示出来。Go语言中没有类似于 implements 的关键字。 Go编译器将自动在需要的时候检查两个类型之间的实现关系。

***总结: **

  1. 接口的方法与实现接口的类型方法格式(方法中的名称、参数列表、返回参数列表)一致。

  2. 必须实现该接口的所有抽象方法。

  3. 一个类型可以实现多个接口,例如socket同时实现了io.Closerio.Writer接口。

  4. 多个类型可以实现相同的接口。

    Golang的接口实现是隐式实现的,无需让实现接口的类型显示表示出实现了那些接口。这个涉及被称为非侵入式涉及

3 嵌套接口

一个接口可以包含一个或多个其他的接口,这相当于直接将内嵌接口的方法列举在外层接口中的一样。

4 类型断言

Java当中有instanceof这样的关键字判断类型 Go当中自然也有相应的方法来判断类型 ,类型断言是作用在接口值上的操作,写出来类似于x.(T),其中x是一个接口类型的表达式,T则是一个具体类型(称为断言类型)


如果断言类型T是一个具体类型,那么类型断言就会检查x的动态类型是否为T。如果检查成功,则类型断言的结果就是x的动态类型

写法为value, ok := em.(T) 如果确保em 是同类型的时候可以直接使用value:=em.(T)一般用于switch语句中。

变量名 含义
em 代表要判断的变量
T 代表要判断的变量
value 代表返回的类型
ok 代表是否为同一类型

*注意:

  1. em必须为interface类型才可以进行类型断言;如果不是则可以使用interface{}来强转。

  2. 当函数作为参数并且被调用函数将参数类型指定为interface{}的时候是没有办法直接调用该方法的。

    type FuncTyte func(s string) int
    
    func PrintFunc(s string) int{
    fmt.Println(s)
    return 1
    } func JudgeFuncType(v interface{}){
    //FuncTyte(v)("hello") # 报错 需要先判断v的类型是否是FuncType
    //fmt.Println(reflect.TypeOf(v))
    if _func, ok := v.(FuncTyte); ok{
    _func("hello")
    }
    } func main() {
    JudgeFuncType(FuncTyte(PrintFunc))
    }

例子:

// w是一个接口类型
var w io.Writer
// os.Stdin是一个*os.File类型,该类型实现了io.Writer接口中的Write方法
w = os.Stdin
// *os.File是一个具体的引用类型,而非接口类型
f := w.(*os.File) // 成功 f==os,Stdin
fmt.Print(f, reflect.TypeOf(f)) // &{0xc00006e000} *os.File
k := w.(*bufio.Reader) // 报错,因为w是一个*os.File类型

如果断言类型T是一个接口类型,那么类型断言就会检查x的动态类型是否满足T, 即x要实现T中所有抽象方法。成功则返回x

例子:

var w io.Writer
w = os.Stdout
f1 := w.(io.ReadWriter) // 成功, *os.File类型(w)实现了io.ReadWriter接口
f2 := w.(io.ReadWriteCloser) // 成功
f3 := w.(io.ByteReader) // 报错, *os.File类型(w)未实现io.ReadByte接口

除此之外,如果类型A实现了接口B,注意看到底是指针类型A还是非指针类型A实现的

package main

import "fmt"
type A interface {
IsA(b []byte) int
}
type Aa struct {
name string
}
func (sq Aa) IsA(b []byte) int {
return 1
}
func main() {
s1 := &Aa{"aa"}
s2 := Aa{"aa"}
fmt.Printf("S1:\t%T\n", interface{}(s1).(A)) // S1: *main.Aa
fmt.Printf("S2:\t%T", interface{}(s2).(A)) // S2: main.Aa
}

比如上述是Aa类型实现了接口A, 类型断言结果默认获取的是其动态类型,是指针就是指针,是非指针就是非指针,但是如果是*Aa类型实现了接口A,其非指针类型进行类型断言就会出现运行时错误“interface conversion: main.Aa is not main.A: missing method IsA”,一般对于基本数据类型使用非指针来实现接口,其余类型尽量使用指针。

5 空接口类型

一个类型如果实现了接口中所有方法,则称该类型实现了该接口;那同样,任何类型都隐式地实现了空接口。

空接口能够保存任何类型的值,也可以从空接口中取出值

空接口类型就类似于java中的Object对象,任何实现类的超类。

var a interface{} = 1
var b int = a.(int) // 必须类型断言, 否则出错
fmt.Println(b)

此外,值得注意的是,对于空接口类型的值为动态类型的是不可比较的.

动态类型:map、slice

非动态类型:channal、int、string、[10]int{}、函数、struct

var a interface{} = []int{1, 2, 3}
var b interface{} = []int{3, 4, 5}
fmt.Println(a==b)
//panic: runtime error: comparing uncomparable type []int

6 空接口类型实现字典

空接口可以保存任何类型这个特性可以方便地用于容器的设计。下面例子使用 map 和 interface{} 实现了一个字典。字典在其他语言中的功能和 map 类似,可以将任意类型的值做成键值对保存,然后进行找回、遍历操作。详细实现代码如下所示。

package main

// 字典结构
type Dictionary struct {
data map[interface{}]interface{}
} // 创建字典
func NewDictionary() *Dictionary{
dict := &Dictionary{}
dict.Clear()
return dict
} // 清空字典
func (dict *Dictionary) Clear() {
dict.data = make(map[interface{}]interface{})
} // Set
func (dict *Dictionary) Set(key , value interface{}){
dict.data[key] = value
} // Get
func (dict *Dictionary) Get(key interface{}) interface{}{
return dict.data[key]
} // 遍历(使用回调方式)
func (dict *Dictionary) Visit(callback func(k,v interface{}) bool){ if callback == nil{
return
} else {
for key, value := range dict.data{
if !callback(key, value) {
return
}
}
}
}

7 类型分支(type-switch)

type-switch流程控制的语法与switch-case流程控制代码块有些相似,

一个type-switch流程控制代码块的语法如下所示:

switch 接口变量.(type) {
case 类型1:
// 变量是类型1时的处理
case 类型2:
// 变量是类型2时的处理

default:
// 变量不是所有case中列举的类型时的处理
}
var w io.Writer
w = os.Stdout
switch w.(type) {
case *os.File:
fmt.Println("os.file")
case nil:
fmt.Println("nil")
default:
fmt.Println("error")
}

一个例子:

// Alipay手机支付
type Alipay struct {
money string
} // cash现金支付
type Cash struct {
money string
} // 支持刷脸支付
type PayUseFaceID interface {
UseFaceID() (success int, err error)
} // 使用假票情况
type UseArtifficialMoney interface {
UseFakeMoney() (flag int)
} // Alipay支持面容支付
func (alipay Alipay) UseFaceID() (success int, err error) {
return 1, nil
} // 现金支付可能会使用假币
func (cash *Cash) UseFakeMoney() (flag int){
return 1
} func judgeMethod(method interface{}){
switch method.(type) {
case PayUseFaceID:
fmt.Println("alipay 刷脸支付")
case UseArtifficialMoney:
fmt.Println("现金支付使用假币")
default:
fmt.Println("不支持的方式")
}
} func main() { judgeMethod(new(Alipay))
fmt.Println("--------------")
judgeMethod(new(Cash)) } //alipay 刷脸支付
//--------------
//现金支付使用假币

在这个例子中定义了两种支付方式的类型,一种是Alipay,另一种是Cash,同时又定义了两种特征的接口,第一个是刷脸支付PayUseFaceID特征,另一个是使用假币UseArtifficialMoney的特征。这里,Alipay类型实现了接口PayUseFaceID

8 空接口和函数重载

在 Go语言中函数重载可以用可变参数 ...T 作为函数最后一个参数来实现。如果我们把 T 换为空接口,那么可以知道任何类型的变量都是满足 T (空接口) 类型的,这样就允许我们传递任何数量任何类型的参数给函数,即重载的实际含义。

9 接口继承

在golang中,接口被设计为实现多态的最佳方式,

当一个类型包含(内嵌)另一个类型(实现了一个或多个接口)的指针时,这个类型就可以使用(另一个类型)所有的接口方法。

Golang 接口的更多相关文章

  1. 【Golang 接口自动化06】微信支付md5签名计算及其优化

    前言 可能看过我博客的朋友知道我主要是做的支付这一块的测试工作.而我们都知道现在比较流行的支付方式就是微信支付和支付宝支付,当然最近在使用低手续费大力推广的京东金融(已改名为京东数科)以后也可能站到第 ...

  2. 【Golang 接口自动化00】为什么要用Golang做自动化?

    为什么使用Golang做自动化 顺应公司的趋势学习了Golang之后,因为没有开发那么多的时间和项目来实践,怕步此前学习Java缺少练习遗忘殆尽的后尘,决定利用工作之余的时间把此前用Python的写的 ...

  3. Golang 接口与反射知识要点

    目录 Golang 接口与反射知识要点 1. 接口类型变量 2. 类型断言 3. 鸭子类型 4. 反射机制 5. reflect 包 TypeOf().ValueOf() Type().Kind() ...

  4. Golang接口(interface)三个特性(译文)

    The Laws of Reflection 原文地址 第一次翻译文章,请各路人士多多指教! 类型和接口 因为映射建设在类型的基础之上,首先我们对类型进行全新的介绍. go是一个静态性语言,每个变量都 ...

  5. golang接口三个特性

    类型和接口 因为映射建设在类型的基础之上,首先我们对类型进行全新的介绍.go是一个静态性语言,每个变量都有静态的类型,因此每个变量在编译阶段中有明确的变量类型,比如像:int.float32.MyTy ...

  6. Golang接口简单了解

    在Golang中,一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口. package main import "fmt" type Animal interface ...

  7. 【Golang 接口自动化08】使用标准库httptest完成HTTP请求的Mock测试

    前言 Mock是一个做自动化测试永远绕不过去的话题.本文主要介绍使用标准库net/http/httptest完成HTTP请求的Mock的测试方法. 可能有的小伙伴不太了解mock在实际自动化测试过程中 ...

  8. 【Golang 接口自动化03】 解析接口返回XML

    上一篇我们学习了怎么发送各种数据类型的http请求,这一篇我们来介绍怎么来解析接口返回的XML的数据. 解析接口返回数据 定义结构体 假设我们现在有一个接口返回的数据resp如下: <?xml ...

  9. 【Golang 接口自动化02】使用标准库net/http发送Post请求

    写在前面 上一篇我们介绍了使用 net/http 发送get请求,因为考虑到篇幅问题,把Post单独拎了出来,我们在这一篇一起从源码来了解一下Golang的Post请求. 发送Post请求 net/h ...

随机推荐

  1. 解决TensorFlow在terminal中正常但在jupyter notebook中报错的方案

    报错情况: # 本地运行正常,jupyter中无法 import tensorflow ImportError: libcublas.so.10.0: cannot open shared objec ...

  2. Java自学-接口与继承 多态

    Java的多态 操作符的多态 +可以作为算数运算,也可以作为字符串连接 类的多态 父类引用指向子类对象 示例 1 : 操作符的多态 同一个操作符在不同情境下,具备不同的作用 如果+号两侧都是整型,那么 ...

  3. 如何快速找到Chrome配置文件路径,MAC 与window 都适用

    Chrome 的配置文件主要用于存储浏览器的相关配置.书签.扩展插件和密码等,Chrome 配置文件会存储在用户计算机的一个单独文件夹当中,当你升级或重装浏览器时,这些已有配置将可以被完整保存下来. ...

  4. element-ui文件上传 做类型大小的限制

    上代码: <div class="filebox"> <el-upload class="upload-demo" :action=" ...

  5. CSS 用法和特性

    一.CSS 基本用法 1.CSS 样式语法 样式是 CSS 最小的语法单元,每个样式包含两部分内容:选择器和声明(规则). 语法: p {font-size:12px; color:#333} 注意: ...

  6. NIO与网络编程系统化学习

    1.背景 数据在网络中传输,必然回遇到读写问题.... 2.比较NIO与IO 3.案例演示 3.1.缓冲区演示 package com.wfd360.nio; import org.junit.Tes ...

  7. java Elasticsearch 进行嵌套子聚合

    聚合子查询: TermsAggregationBuilder aggregation = AggregationBuilders.terms("dt_id").field(&quo ...

  8. 【JMeter】压力测试工具的概览与使用

    软件工程综合实践第五次个人作业 作业要求:在软件测试章节中,我们介绍了不少VSTS的软件测试工具,请使用一些其他平台上的测试工具,并写博客介绍如何在你的项目中具体使用. 前言:   第一次看到这个作业 ...

  9. 性能测试基础---LR关联

    ·什么时候需要做关联?一般来说,在脚本运行出错的时候,我们就可能需要进行关联处理. ·脚本出错分为两种情况: ·直接回放出错(失败).通常来说,如果录制成功,回放失败,排除数据的唯一性约束之后,那就必 ...

  10. HDU3338 Kakuro Extension(最大流+思维构图)

    这道题一定要写一下,卡了好久. 题意: 有黑白两种方格,最上边一行和最左边一列一定是黑色,然后其余的地方有可能是黑色,有可能是白色,和白色相邻的黑色方格里有数字(1个或2个), 现在要求在白色方格里填 ...