前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Go语言学习(九)| 接口

Go语言学习(九)| 接口

作者头像
Mervyn
发布于 2020-07-21 06:54:43
发布于 2020-07-21 06:54:43
29800
代码可运行
举报
运行总次数:0
代码可运行

接口是一种约定,它是一个抽象的类型, 和我们见到的具体的类型如 int 、 map 、 slice 等不一样。 具体的类型, 我们可以知道它是什么, 并且可以知道可以用它做什么; 但是接口不一样, 接口是抽象的, 它只有一组接口方法, 我们并不知道它的内部实现, 所以我们不知道接口是什么, 但是我们知道可以利用它提供的方法做什么。

接口的定义

接口一般这样定义:接口定义一个对象的行为。

这里定义了一个有两个方法的接口I:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type I interface {
    Get() int
    Put(int)
}

下面定义了具有一个字段和两个方法的结构类型S

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type S struct { i int }
func (p *S) Get() int { return p.i }
func (p *S) Put(v int) { p.i = v }

对于接口 I , S 是合法的实现,因为它定义了 I 所需的两个方法。注意,即便是没有明确定义 S 实现了 I ,这也是正确的。 interface 类型定义了一组方法,如果某个对象实现了某个接口的所有方法,则次对象就实现了此接口。

如果我们定义了一个 interface 的变量,那么这个变量里面可以存实现这个 interface 的任意类型的对象。 例如下面例子中,我们定义了一个 Men interface 类型的变量 m ,那么 m 里面可以存 Human 、 Student 或者 Employee 值.例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main
import "fmt"

type Human struct {
    name string
    age int
    phone string
}

type Student struct {
    Human //匿名字段
    school string
    loan float32
}

type Employee struct {
    Human //匿名字段
    company string
    money float32
}

//Human实现SayHi方法
func (h Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

//Human实现Sing方法
func (h Human) Sing(lyrics string) {
    fmt.Println("La la la la...", lyrics)
}

//Employee重载Human的SayHi方法
func (e Employee) SayHi() {
    fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
        e.company, e.phone)
    }

// Interface Men被Human,Student和Employee实现
// 因为这三个类型都实现了这两个方法
type Men interface {
    SayHi()
    Sing(lyrics string)
}

func main() {
    mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
    paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
    sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
    Tom := Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000}

    //定义Men类型的变量i
    var i Men

    //i能存储Student
    i = mike
    fmt.Println("This is Mike, a Student:")
    i.SayHi()
    i.Sing("November rain")

    //i也能存储Employee
    i = Tom
    fmt.Println("This is Tom, an Employee:")
    i.SayHi()
    i.Sing("Born to be wild")

    //定义了slice Men
    fmt.Println("Let's use a slice of Men and see what happens")
    x := make([]Men, 3)
    //这三个都是不同类型的元素,但是他们实现了interface同一个接口
    x[0], x[1], x[2] = paul, sam, mike

    for _, value := range x{
        value.SayHi()
    }
}

空 interface

interface{} 不包含任何的 method ,它可以存储任意类型的数值。fmt的源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type Stringer interface{
    String() string
}

任何实现了 String 方法的类型都能作为参数被 fmt.Println 调用如果需要某个类型能fmt包以特殊的格式输出,就必须实现 Stringer 这个接口。例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main
import (
    "fmt"
    "strconv"
)
type Human struct {
    name string
    age int
    phone string
}

//通过此方法Human实现了fmt.Stringer
func (h Human) String() string {
    retrun h.name + " - " + strconv.Itoa(h.age) + " years - " + h.phone
}

func main(){
    Bob := Human{"Bob", 39, "000-7777-xxx"}
    fmt.Println("This Human is:", Bob)
}

接口值

例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func f(p I) {                //定义一个函数接受一个接口类型作为参数
    fmt.Println(p.Get())     //p实现了接口I,必须有Get()方法
    p.Put(1)                 //Put()方法是类似的
}

这里的变量 p 保存了接口类型的值。因为 S 实现了 I,可以调用 f 向其传递 S 类型的值的指针:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var s S
f(&s)

获取 s 的地址,而不是 S 的值的原因,是因为在 s 的指针上定义了方法,参阅上面的代码。这并不是必须的——可以定义让方法接受值——但是这样的话 Put 方法就不会像期望的那样工作了。实际上,无须明确一个类型是否实现了一个接口意味着 Go 实现了叫做 duck typing 的模式。这不是纯粹的 duck typing ,因为如果可能的话 Go 编译器将对类型是否实现了接口进行实现静态检查。

假设需要在函数 f 中知道实际的类型。在 Go 中可以使用 type switch 得到。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func f(p I) {
    switch t := p.(type) { //类型判断。在switch语句中使用(type)。保存类型到变量t;
    case *S: //p的实际类型是S的指针;
    case *R: //p的实际类型是R的指针;
    case S: //p的实际类型是S;
    case R: //p的实际类型是R;
    default: //实现了I的其他类型。
    }
}

注意element.(type) 语法不能在 switch 外的任何逻辑里面使用,如果你要在 switch 外面判断一个类型就使用 comma-ok。

类型判断不是唯一的运行时得到类型的方法。为了在运行时得到类型,同样可以使用 comma, ok 来判断一个接口类型是否实现了某个特定接口:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if t, ok := something.(I); ok {
    // 对于某些实现了接口I 的
    // t 是其所拥有的类型
}

Comma-ok断言

Go语言里面有一个语法,可以直接判断是否是该类型的变量:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
value, ok = element.(T)

这里 value 就是变量的值, ok 是一个 bool 类型, element 是 interface 变量, T 是断言的类型。如果 element 里面确实存储了 T 类型的数值,那么 ok 返回 true ,否则返回 false 。例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import (
    "fmt"
    "strconv"
)

type Element interface{}
type List [] Element

type Person struct {
    name string
    age int
}

//定义了String方法,实现了fmt.Stringer
func (p Person) String() string {
    return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"
}

func main() {
    list := make(List, 3)
    list[0] = 1 // an int
    list[1] = "Hello" // a string
    list[2] = Person{"Dennis", 70}

    for index, element := range list {
        if value, ok := element.(int); ok {
            fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
        } else if value, ok := element.(string); ok {
            fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
        } else if value, ok := element.(Person); ok {
            fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
        } else {
            fmt.Println("list[%d] is of a different type", index)
        }
    }
}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
list[0] is an int and its value is 1
list[1] is a string and its value is Hello
list[2] is a Person and its value is (name: Dennis - age: 70 years)

确定一个变量实现了某个接口,可以使用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
t := something.(I)

由于每个类型都能匹配到空接口: interface{} 。我们可以创建一个接受空接口作为参数的普通函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func g(something interface{}) int {
    return something.(I).Get()
}

在这个函数中的 return something.(I).Get() 是有一点窍门的。值 something 具有类型 interface{} ,这意味着方法没有任何约束:它能包含任何类型。 .(I) 是类型断言,用于转换 something 到 I 类型的接口。如果有这个类型,则可以调用 Get() 函数。因此,如果创建一个 *S 类型的新变量,也可以调用 g() ,因为 *S 同样实现了空接口。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
s = new(S)
fmt.Println(g(s));

例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main
import (
    "fmt"
)
func main(){
    s  := new(S)
    ss := "Hello world"
    fmt.Printf("%d\n",g(s))
    fmt.Printf("%s\n",demo(ss))
}
func g(something interface{}) int {
    return something.(I).Get()
}
func demo(something interface{}) string {
    return something.(string)
}
type I interface {
    Get() int
    Put(int)
}
type S struct { i int }
func (p *S) Get() int {
    return p.i
}
func (p *S) Put(v int) {
    p.i = v
}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
0
Hello world

例2:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main
import (
    "fmt"
)
func main(){
    s  := S{1}
    fmt.Printf("%d\n",g(s))
}
func g(something S) int {
    return something.Get()
}
type S struct { i int }
func (p *S) Get() int {
    return p.i
}
func (p *S) Put(v int) {
    p.i = v
}

输出结果:1

接口名字

根据规则,单方法接口命名为方法名加上 er后缀:如 ReaderWriterFormatter 等。有一堆这样的命名,高效的反映了它们职责和包含的函数名。 ReadWriteCloseFlushString 等等有着规范的声明和含义。为了避免混淆,除非有类似的声明和含义,否则不要让方法与这些重名。相反的,如果类型实现了与众所周知的类型相同的方法,那么就用相同的名字和声明;将字符串转换方法命名为 String 而不是 ToString 。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-01-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发技术那些事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
JavaScript事件探秘
事件流描述的是从页面中接收事件的顺序。IE的事件流是事件冒泡流,而Netscape的事件流是事件捕获流
张张
2019/12/26
9370
【JS】395-重温基础:事件
事件流描述的是从页面中接收事件的顺序,通常有这样两种完全相反的事件流概念:事件冒泡流(IE团队提出)和事件捕获流(网景团队提出)。
pingan8787
2019/11/05
1.1K0
事件
事件 JavaScript和HTML的交互是通过事件实现的。JavaScript采用异步事件驱动编程模型,当文档、浏览器、元素或与之相关对象发生特定事情时,浏览器会产生事件。如果JavaScript关注特定类型事件,那么它可以注册当这类事件发生时要调用的句柄。事件是某个行为或者触发,比如点击、鼠标移动..... 当用户点击鼠标时 当网页已加载时 当图像已加载时 当鼠标移动到元素上时 当用户触发按键时... 事件流 事件流描述的是从页面中接收事件的顺序,比如有两个嵌套的div,点击了内层的div,这时候是内层
小胖
2018/06/27
1.4K0
事件
JavaScript与HTML之间的交互式通过事件实现的。 事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器(或处理程序)来预订事件,以便事件发生时执行相应的代码。
奋飛
2019/08/15
3.4K0
JavaScript 学习-30.HTML DOM0级事件和 DOM2级事件
# 前言 DOM0 级事件就是 html 元素添加onclick 属性,或者给元素添加onclick事件,但是同元素的同一事件只能绑定一个函数,否则后面的事件会覆盖前面的事件。 如果我们想一个元素绑定多次同一个时间,比如我想绑定2个onclick事件,2个都要生效,于是就有了DOM2 级事件,通过addEventListener绑定的事件。 为什么没有 DOM1 级事件,我也很好奇,DOM1 一般只有设计规范没有具体实现,所以一般没有所谓的DOM1 级事件。 DOM0 级事件 DOM0 级事件就是前面讲到的
上海-悠悠
2022/05/30
1.1K0
JavaScript 学习-30.HTML DOM0级事件和  DOM2级事件
事件监听函数,以及事件的捕获和冒泡机制
事件一般是用于浏览器和用户操作之间的交互,当用户执行某些特殊的操作时,浏览器给予反应,触发绑定的事件,事件流,事件发生时会在元素节点和根节点之间按照约定的顺序传播,事件经过的所有节点都会受到事件的影响,这个传播过程被称为DOM事件流
子舒
2022/06/09
1.3K0
事件监听函数,以及事件的捕获和冒泡机制
js 事件笔记
在Web中, 事件在浏览器窗口中被触发,执行事先绑定的事件处理器(也就是事件触发时会运行的代码块),对事件做出响应。 用户在浏览器的任何一个操作都会去触发一个事件,JavaScript采用异步事件驱动编程模型,当文档、浏览器、元素或与之相关对象发生特定事情时,浏览器会产生事件。
bamboo
2019/01/29
11.4K0
js 事件笔记
JavaScript事件处理程序
事件就是用户或者浏览器执行的某种操作。我们常用的点击,滚动视口,鼠标滑动都是事件,为响应事件而调用的函数被称为事件处理程序,在js中事件处理程序的名字以 on 开头。
大熊G
2022/11/14
6170
JavaScript事件
JavaScript事件 对于事件来讲,首先,我们需要了解这样几个概念:事件;事件处理程序;事件类型;事件流;事件冒泡;事件捕获;事件对象;事件模拟,事件方面的性能优化(事件委托、移除事件处理程序); 事件的概念 事件:指的是文档或者浏览器窗口中发生的一些特定交互瞬间。我们可以通过监听器(或者处理程序)来预定事件,以便事件发生的时候执行相应的代码。 事件处理程序:我们用户在页面中进行的点击这个动作,鼠标移动的动作,网页页面加载完成的动作等,都可以称之为事件名称,即:click、mousemove、loa
汤高
2018/01/11
2.1K0
JavaScript事件
关于DOM事件流、DOM0级事件与DOM2级事件
DOM 事件模型包括捕获和冒泡,捕获是从上往下到达目标元素,冒泡是从当前元素,也就是目标元素往上到 window
Leophen
2019/08/26
2.2K0
JavaScript的事件
javascript与HTML之间的交互是通过事件实现的。事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。
小小鱼儿小小林
2020/06/24
1.6K0
JS事件流模型
事件捕获Event Capturing是一种从上而下的传播方式,以click事件为例,其会从最外层根节向内传播到达点击的节点,为从最外层节点逐渐向内传播直到目标节点的方式。 事件冒泡Event Bubbling是一种从下往上的传播方式,同样以click事件为例,事件最开始由点击的节点,然后逐渐向上传播直至最高层节点。
WindRunnerMax
2020/08/27
1.7K0
面试官:什么是js中的事件流以及事件模型?
我们先从字面意义上理解,事件我们已经知道了是什么,那流呢?我们看看百度对于流的解释
inline705
2022/03/01
2.1K0
面试官:什么是js中的事件流以及事件模型?
02-老马jQuery教程-jQuery事件处理
根据文章内容总结摘要。
老马
2017/12/27
6.6K0
02-老马jQuery教程-jQuery事件处理
深入理解事件
javascript 给 DOM 绑定事件处理函数总的来说有2种方式:在 html 文档中绑定、在 js 代码中绑定。下面的方式1、方式2属于在 html 中绑定事件,方式3、方式4和方式5属于在js代码中绑定事件,其中,方式4和5属于事件监听,而方式5是最推荐的做法。
Chor
2019/11/08
8830
深入理解浏览器事件模型的概念和原理
大家好,我是腾讯云开发者社区的 Front_Yue,本篇文章将带领大家深入探讨浏览器事件模型的概念和原理
Front_Yue
2024/01/29
8191
深入理解浏览器事件模型的概念和原理
JavaScript第十一弹——事件流!事件代理!我懂了!
Hello小伙伴们,好久不见,最近实在太忙了,所以没有更!今天回来更文突然发现了一件尴尬的事情,两个第九弹哇,好像也改不了题目呀,那我们今天只能直接第十一弹了
萌兔IT
2019/07/25
4970
JavaScript第十一弹——事件流!事件代理!我懂了!
深入理解 DOM 事件机制
本文主要介绍 DOM 事件级别、DOM 事件模型、事件流、事件代理和 Event 对象常见的应用,希望对你们有些帮助和启发!
小生方勤
2019/06/02
2.9K1
Web开发的基本功
#ThoughtWorkers好声音#第十五期 (图片:网络) 有些东西称为基本功,对于 Web 开发而言,事件处理模型便是其中的一个,我们经常会在代码里遇到阻止浏览器默认行为的做法。 成都办公室
ThoughtWorks
2018/04/16
1.4K0
Web开发的基本功
JS事件流
HTML中与javascript交互是通过事件驱动来实现的,例如鼠标点击事件、页面的滚动事件onscroll等等,可以向文档或者文档中的元素添加事件侦听器来预订事件。想要知道这些事件是在什么时候进行调用的,就需要了解一下“事件流”的概念。
前端逗逗飞
2021/04/30
8.7K0
JS事件流
相关推荐
JavaScript事件探秘
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档