前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >13.Go语言-接口

13.Go语言-接口

原创
作者头像
面向加薪学习
发布2022-09-04 10:49:53
2450
发布2022-09-04 10:49:53
举报
文章被收录于专栏:面向加薪学习面向加薪学习

第 13 章

在 Go 语言中, 接口 就是方法签名(Method Signature)的集合。在面向对象的领域里,接口定义一个对象的行为,接口只指定了对象应该做什么,至于如何实现这个行为,则由对象本身去确定。当一个类型实现了接口中的所有方法,我们称它实现了该接口。接口指定了一个类型应该具有的方法,并由该类型决定如何实现这些方法。

13.1 接口的定义

使用 type 关键字可以定义接口:

代码语言:go
复制
type interface_name interface {
    method()
}

13.2 接口的实现

创建类型或者结构体,并为其绑定接口定义的方法,接收者为该类型或结构体,方法名为接口中定义的方法名,这样就说该类型或者结构体实现了该接口。例如:

代码语言:go
复制
package main

import "fmt"

type Study interface {
    learn()
}

type Student struct {
    name string
}

func (s Student) learn() {
    fmt.Printf("%s 在读 %s", s.name, s.book)
}

func main() {
    student1 := Student{
        name: "张三",
        book: "《Go语言极简一本通》",
    }
    student1.learn()
}

上面的程序定义了一个名为 Study 的接口,接口中有未实现的方法 learn() ,这里还定义了名为 Student 的结构体,其绑定了方法 learn() ,也就隐式实现了 Study 接口,实现的内容是打印语句。

上面的例子使用了值接受者实现接口,下面的例子使用了指针接受者实现接口。

代码语言:go
复制
package main

import "fmt"

type Study interface {
    learn()
}

...

type Worker struct {
    name string
    book string
    by   string
}

func (w *Worker) learn() {
    fmt.Printf("%s 在读 %s,通过方式 %s", w.name, w.book, w.by)
}

func main() {
    var s1 Study
    var s2 Study

    student2 := Student{
        name: "李四",
        book: "《Go语言极简一本通》",
    }
    s1 = student2
    s1.learn()

    student3 := Student{
        name: "王五",
        book: "Go语言微服务架构核心22讲",
    }
    s1 = &student3
    s1.learn()

    worker1 := Worker{
        name: "老王",
        book: "从0到Go语言微服务架构师",
        by:   "视频",
    }
    // s2 = worker1 // error
    s2 = &worker1
    s2.learn()
}

该程序定义了结构体 Student ,使用其作为值接受者实现 Study 接口。student2 的类型为 Studentstudent2 赋值给 s1 ,由于 Student 实现了接口变量 s1 所以会有输出。而接下来 s1 又被赋值为 &student3 ,同样有输出。接下来的结构体 Worker 使用指针接受者实现 Study 接口。worker1 的类型为 Workers2 被赋值为 &worker1 ,所以会有输出。但如果把 s2 赋值为 worker1 会报错,对于使用指针接受者的方法,用一个指针或者一个可取得地址的值来调用都是合法的。但接口中存储的具体值(Concrete Value)并不能取到地址,因此对于编译器无法自动获取 worker1 的地址,于是程序报错。

13.3 接口实现多态

使用接口可以实现多态,例如下面的程序,定义了名为 Study 的接口,接口中有方法 learn() 。程序中还定义了结构体 StudentWorker ,分别实现了 Study 接口,Student 的 learn name: "李四", book: "《Go语言极简一本通》" 而 Worker 的 learn 为 name: "张三",book: "从0到Go语言微服务架构师",by: "视频" ,利用的接口实现了不同的功能,这就是多态。

代码语言:go
复制
package main

func main() {
    ...
    s2.learn()
    worker1.learn()
}

13.4 接口的内部表示

可以把接口的内部看做 (type, value)type 是接口底层的具体类型(Concrete Type),而 value 是具体类型的值。

代码语言:go
复制
package main

import "fmt"

...

func ShowInterface(s Study) {
    fmt.Printf("接口类型: %T\n,接口值: %v\n", s, s)
}

func main() {
    var s Study
    s = student2
    ShowInterface(s)
    s.learn()
}

在上面的程序中,定义了 Study 接口,其中有 learn() 方法,结构体 Student 实现了该接口。使用 s = student2 语句我们把 student2 ( Student 类型)赋值给了 s ( Study 类型),现在打印出 Study 的具体类型为 Student ,而 student2 的值为 name: "李四", book: "《Go语言极简一本通》"

13.5 空接口

空接口 是特殊形式的接口类型,没有定义任何方法的接口就称为空接口,可以说所有类型都至少实现了空接口,空接口表示为 interface{} 。例如,我们之前的写过的空接口参数函数,可以接受任何类型的参数:

代码语言:go
复制
package main

import "fmt"

func ShowType(i interface{}) {
    fmt.Printf("类型: %T, 值: %v\n", i, i)
}

func main() {
    str := "从0到Go语言微服务架构师"
    ShowType(str)
    num := 3.14
    ShowType(num)
}

上面的程序中我们定义了函数 ShowType 使用空接口作为参数,所以可以给这个函数传递任何类型的参数。

通过上面的例子不难发现接口都有两个属性,一个是值,而另一个是类型。对于空接口来说,这两个属性都为 nil

代码语言:go
复制
package main

import "fmt"

func main() {
    var i interface{}
    fmt.Printf("Type: %T, Value: %v", i, i)
    // Type: <nil>, Value: <nil>
}

除了上面讲到的使用空接口作为函数参数的用法,空接口还有以下两种用法。

直接使用 interface{} 作为类型声明一个实例,这个实例就能承载任何类型的值:

代码语言:go
复制
package main

import "fmt"

func main() {
    var i interface{}

    i = "从0到Go语言微服务架构师"
    fmt.Println(i) // Let's go

    i = 3.14
    fmt.Println(i) // 3.14
}

我们也可以定义一个接收任何类型的 arrayslicemapstrcut 。例如:

代码语言:go
复制
package main

import "fmt"

func main() {
    x := make([]interface{}, 3)
    x[0] = "从0到Go语言微服务架构师"
    x[1] = 3.14
    x[2] = []int{1, 2, 3}
    for _, value := range x {
        fmt.Println(value)
    }
}

空接口可以承载任何值,但是空接口类型的对象是不能赋值给另一个固定类型对象的。

代码语言:go
复制
package main

func main() {
    var num = 1
    var i interface{} = num
    var str string = i // error
}

当空接口承载数组和切片后,该对象无法再进行切片。

代码语言:go
复制
package main

import "fmt"

func main() {
    var s = []int{1, 2, 3}

    var i interface{} = s

    var s2 = i[1:2] // error
    fmt.Println(s2)
}

13.6 类型断言

类型断言用于提取接口的底层值(Underlying Value)。使用 interface.(Type) 可以获取接口的底层值,其中接口 interface 的具体类型是 Type

代码语言:go
复制
package main

import "fmt"

func assert(i interface{}) {
    value, ok := i.(int)
    fmt.Println(value, ok)
}

func main() {
    var x interface{} = 3
    assert(x)
    var y interface{} = "从0到Go语言微服务架构师"
    assert(y)
}

13.7 类型选择

类型选择用于将接口的具体类型与 case 语句所指定的类型进行比较。它其实就是一个 switch 语句,但在 switch 后面跟的是 i.(type) ,并且每个 case 后面跟的是类型。

代码语言:go
复制
package main

import "fmt"

func getTypeValue(i interface{}) {
    switch i.(type) {
    case int:
        fmt.Printf("Type: int, Value: %d\n", i.(int))
    case string:
        fmt.Printf("Type: string, Value: %s\n", i.(string))
    default:
        fmt.Printf("Unknown type\n")
    }
}

func main() {
    getTypeValue(300)
    getTypeValue("从0到Go语言微服务架构师")
    getTypeValue(true)
}

13.8 实现多个接口

类型或者结构体可以实现多个接口,例如:

代码语言:go
复制
package main

import "fmt"

...


type Happy interface {
	rest()
}

func (s Student) rest() {
    fmt.Printf("%s 放学了,出去玩...", s.name)
}

func (w *Worker) rest() {
    fmt.Printf("%s 下班了,吃大餐去...", w.name)
}

func main() {
    worker2 := Worker{
        name: "小明",
        book: "从0到Go语言微服务架构师",
        by:   "视频",
    }
    worker2.learn()
    worker2.rest()
}

13.9 接口的嵌套

虽然在 Go 中没有继承机制,但可以通过接口的嵌套实现类似功能。例如:

代码语言:go
复制
package main

import "fmt"

...

type Life interface {
    Study
    Happy
}

func main() {
    worker2 := Worker{
        name: "小明",
        book: "从0到Go语言微服务架构师",
        by:   "视频",
    }
    worker2.learn()
    worker2.rest()
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第 13 章
    • 13.1 接口的定义
      • 13.2 接口的实现
        • 13.3 接口实现多态
          • 13.4 接口的内部表示
            • 13.5 空接口
              • 13.6 类型断言
                • 13.7 类型选择
                  • 13.8 实现多个接口
                    • 13.9 接口的嵌套
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档