千家信息网

Go interface怎么使用

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,本篇内容介绍了"Go interface怎么使用"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!什么是
千家信息网最后更新 2026年11月02日Go interface怎么使用

本篇内容介绍了"Go interface怎么使用"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

什么是interface

简单的说,interface是一组method的组合,我们通过interface来定义对象的一组行为。

interface类型

interface类型定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了此接口。详细的语法参考下面这个例子

type Human struct {    name string    age int    phone string}type Student struct {    Human //匿名字段Human    school string    loan float32}type Employee struct {    Human //匿名字段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 la, la la la la la...", lyrics)}//Human对象实现Guzzle方法func (h *Human) Guzzle(beerStein string) {    fmt.Println("Guzzle Guzzle Guzzle...", beerStein)}// 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) //此句可以分成多行}//Student实现BorrowMoney方法func (s *Student) BorrowMoney(amount float32) {    s.loan += amount // (again and again and...)}//Employee实现SpendSalary方法func (e *Employee) SpendSalary(amount float32) {    e.money -= amount // More vodka please!!! Get me through the day!}// 定义interfacetype Men interface {    SayHi()    Sing(lyrics string)    Guzzle(beerStein string)}type YoungChap interface {    SayHi()    Sing(song string)    BorrowMoney(amount float32)}type ElderlyGent interface {    SayHi()    Sing(song string)    SpendSalary(amount float32)}

通过上面的代码我们可以知道,interface可以被任意的对象实现。我们看到上面的Men interface被Human、Student和Employee实现。同理,一个对象可以实现任意多个interface,例如上面的Student实现了Men和YoungChap两个interface。

最后,任意的类型都实现了空interface(我们这样定义:interface{}),也就是包含0个method的interface。

interface值

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

因为m能够持有这三种类型的对象,所以我们可以定义一个包含Men类型元素的slice,这个slice可以被赋予实现了Men接口的任意结构的对象,这个和我们传统意义上面的slice有所不同。

让我们来看一下下面这个例子:

package mainimport "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类型实现,而不能自我实现, Go通过interface实现了duck-typing:即"当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子"。

空interface

空interface(interface{})不包含任何的method,正因为如此,所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method),但是空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。

// 定义a为空接口var a interface{}var i int = 5s := "Hello world"// a可以存储任意类型的数值a = ia = s

一个函数把interface{}作为参数,那么他可以接受任意类型的值作为参数,如果一个函数返回interface{},那么也就可以返回任意类型的值。是不是很有用啊!

interface函数参数

interface的变量可以持有任意实现该interface类型的对象,这给我们编写函数(包括method)提供了一些额外的思考,我们是不是可以通过定义interface参数,让函数接受各种类型的参数。

举个例子:fmt.Println是我们常用的一个函数,但是你是否注意到它可以接受任意类型的数据。打开fmt的源码文件,你会看到这样一个定义:

type Stringer interface {     String() string}

也就是说,任何实现了String方法的类型都能作为参数被fmt.Println调用,让我们来试一试

package mainimport (    "fmt"    "strconv")type Human struct {    name string    age int    phone string}// 通过这个方法 Human 实现了 fmt.Stringerfunc (h Human) String() string {    return "❰"+h.name+" - "+strconv.Itoa(h.age)+" years -  ✆ " +h.phone+"❱"}func main() {    Bob := Human{"Bob", 39, "000-7777-XXX"}    fmt.Println("This Human is : ", Bob)}

现在我们再回顾一下前面的Box示例,你会发现Color结构也定义了一个method:String。其实这也是实现了fmt.Stringer这个interface,即如果需要某个类型能被fmt包以特殊的格式输出,你就必须实现Stringer这个接口。如果没有实现这个接口,fmt将以默认的方式输出。

//实现同样的功能fmt.Println("The biggest one is", boxes.BiggestsColor().String())fmt.Println("The biggest one is", boxes.BiggestsColor())

注:实现了error接口的对象(即实现了Error() string的对象),使用fmt输出时,会调用Error()方法,因此不必再定义String()方法了。

interface变量存储的类型

我们知道interface的变量里面可以存储任意类型的数值(该类型实现了interface)。那么我们怎么反向知道这个变量里面实际保存了的是哪个类型的对象呢?目前常用的有两种方法:

  • Comma-ok断言

    Go语言里面有一个语法,可以直接判断是否是该类型的变量: value, ok = element.(T),这里value就是变量的值,ok是一个bool类型,element是interface变量,T是断言的类型。

    如果element里面确实存储了T类型的数值,那么ok返回true,否则返回false。

    让我们通过一个例子来更加深入的理解。

    package mainimport (    "fmt"    "strconv")type Element interface{}type List [] Elementtype Person struct {    name string    age int}//定义了String方法,实现了fmt.Stringerfunc (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.Printf("list[%d] is of a different type\n", index)        }    }}

    是不是很简单啊,同时你是否注意到了多个if里面,还记得我前面介绍流程时讲过,if里面允许初始化变量。

    也许你注意到了,我们断言的类型越多,那么if else也就越多,所以才引出了下面要介绍的switch。

  • switch测试

    最好的讲解就是代码例子,现在让我们重写上面的这个实现

    package mainimport (    "fmt"    "strconv")type Element interface{}type List [] Elementtype Person struct {    name string    age int}//打印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{        switch value := element.(type) {            case int:                fmt.Printf("list[%d] is an int and its value is %d\n", index, value)            case string:                fmt.Printf("list[%d] is a string and its value is %s\n", index, value)            case Person:                fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)            default:                fmt.Println("list[%d] is of a different type", index)        }    }}

    这里有一点需要强调的是:element.(type)语法不能在switch外的任何逻辑里面使用,如果你要在switch外面判断一个类型就使用comma-ok。

嵌入interface

Go里面真正吸引人的是它内置的逻辑语法,就像我们在学习Struct时学习的匿名字段,多么的优雅啊,那么相同的逻辑引入到interface里面,那不是更加完美了。如果一个interface1作为interface2的一个嵌入字段,那么interface2隐式的包含了interface1里面的method。

我们可以看到源码包container/heap里面有这样的一个定义

type Interface interface {    sort.Interface //嵌入字段sort.Interface    Push(x interface{}) //a Push method to push elements into the heap    Pop() interface{} //a Pop elements that pops elements from the heap}

我们看到sort.Interface其实就是嵌入字段,把sort.Interface的所有method给隐式的包含进来了。也就是下面三个方法:

type Interface interface {    // Len is the number of elements in the collection.    Len() int    // Less returns whether the element with index i should sort    // before the element with index j.    Less(i, j int) bool    // Swap swaps the elements with indexes i and j.    Swap(i, j int)}

另一个例子就是io包下面的 io.ReadWriter ,它包含了io包下面的Reader和Writer两个interface:

// io.ReadWritertype ReadWriter interface {    Reader    Writer}

反射

Go语言实现了反射,所谓反射就是能检查程序在运行时的状态。我们一般用到的包是reflect包。如何运用reflect包,官方的这篇文章详细的讲解了reflect包的实现原理,laws of reflection

使用reflect一般分成三步,下面简要的讲解一下:要去反射是一个类型的值(这些值都实现了空interface),首先需要把它转化成reflect对象(reflect.Type或者reflect.Value,根据不同的情况调用不同的函数)。这两种获取方式如下:

t := reflect.TypeOf(i)    //得到类型的元数据,通过t我们能获取类型定义里面的所有元素v := reflect.ValueOf(i)   //得到实际的值,通过v我们获取存储在里面的值,还可以去改变值

转化为reflect对象之后我们就可以进行一些操作了,也就是将reflect对象转化成相应的值,例如

tag := t.Elem().Field(0).Tag  //获取定义在struct里面的标签name := v.Elem().Field(0).String()  //获取存储在第一个字段里面的值

获取反射值能返回相应的类型和数值

var x float64 = 3.4v := reflect.ValueOf(x)fmt.Println("type:", v.Type())fmt.Println("kind is float64:", v.Kind() == reflect.Float64)fmt.Println("value:", v.Float())

最后,反射的话,那么反射的字段必须是可修改的,我们前面学习过传值和传引用,这个里面也是一样的道理。反射的字段必须是可读写的意思是,如果下面这样写,那么会发生错误

var x float64 = 3.4v := reflect.ValueOf(x)v.SetFloat(7.1)

如果要修改相应的值,必须这样写

var x float64 = 3.4p := reflect.ValueOf(&x)v := p.Elem()v.SetFloat(7.1)

"Go interface怎么使用"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

类型 方法 对象 变量 字段 存储 接口 面的 反射 例子 函数 参数 就是 数值 不同 也就是 语法 鸭子 学习 输出 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技股 互联网核心科技是什么 盈实互联网科技 互联网科技小贷 礼当家互联网科技 互联网科技革命 上海万想互联网科技 衡水科技产业互联网平台 宁夏众和广鑫互联网科技有限公司 彤创互联网科技有限公司 注册互联网科技公司经营范围大全 想开互联网科技公司怎么样 洛阳共同富裕互联网科技有限公司 中和天地互联网科技有限公司 滨湖区互联网智能科技产品有哪些 互联网营销推广黑科技 富国互联网金融科技混合基金 广州燕处互联网科技有限公司 艺电能源互联网科技有限公司招聘 宁波允成互联网科技有限公司 科技赋能下的互联网金融 互联网科技巨头监管 科技制造互联网公司 深圳互联网络科技有限公司 互联网 科技创业 有马互联网科技 太原新华互联网科技学校怎么样 中国的互联网科技企业的崛起 赶超互联网科技 微信多开 杭州壁岐互联网科技有限公司
0