长安链 DApp 开发必学 Go 10

168 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第24天,点击查看活动详情

本文已参与「开源摘星计划」,欢迎正在阅读的你加入。活动链接:github.com/weopenproje…

上一章,介绍了 go 语言中的“接口”(Interface),使用它可以实现类似于其他语言中的“抽象类”的功能。这一章,咱们继续探索,看看它还有哪些特性。

“接口“的值(Interface values)包含了接口初始化时的值,以及它对应的类型。这很好理解,先来看一段代码:

package main

import (
	"fmt"
	"math"
)

type I interface {               // 定义接口 I,它包含函数签名 M
	M()
}

type T struct {                  // 定义结构体 T,以及实现它的 M 方法
	S string
}

func (t *T) M() {
	fmt.Println(t.S)
}

type F float64                   // 定义浮点类型 F,以及实现它的 M 方法

func (f F) M() {
	fmt.Println(f)
}

func main() {
	var i I

	i = &T{"Hello"}           // 初始化结构体 T,注意,上面定义的时候使用了“指针接收者”,就是带了星号(*),所以这里需要获取结构体的指针
	describe(i)               // 打印接口 i 的值和类型
	i.M()

	i = F(math.Pi)            // 初始化浮点类型 F,注意,上面定义的时候使用的是“数值接收者”,所以这里就不用获取指针了。下面的逻辑跟上面相同~
	describe(i)
	i.M()
}

func describe(i I) {
	fmt.Printf("(%v, %T)\n", i, i)
}

运行代码可以看到,“接口”的值就是初始化传入的值,而类型就是我们自定义实现了 M 方法的类型。

在 go 语言中,可以赋值为 nil 的接口:

package main

import "fmt"

type I interface {
	M()
}

type T struct {
	S string
}

func (t *T) M() {
	if t == nil {                    // 处理当值为 nil 时的情况
		fmt.Println("<nil>")
		return
	}
	fmt.Println(t.S)
}

func main() {
	var i I

	var t *T                     // 定义一个值为空的结构体
	i = t                        // 赋值给 i
	describe(i)                  // 代码正确运行,打印出了接口的值和类型
	i.M()

	i = &T{"hello"}
	describe(i)
	i.M()
}

func describe(i I) {
	fmt.Printf("(%v, %T)\n", i, i)
}

但是,一个 nil 接口就是啥都没有(值和类型都无):

package main

import "fmt"

type I interface {
	M()
}

func main() {
	var i I               // 一个 nil 接口
	describe(i)           // 报错,它不包含任何值和类型
	i.M()
}

func describe(i I) {
	fmt.Printf("(%v, %T)\n", i, i)
}

然而,在 go 语言中,还有一种特殊的接口——空接口(empty interface),它的值和类型都为nil。这种接口有啥用呢?哈哈,一般这种都是狠角色,go 语言中的空接口可以赋值任何类型的数据:

package main

import "fmt"

func main() {
	var i interface{}              // 定义一个空接口
	describe(i)

	i = 42                         // 赋值 int 类型的 42
	describe(i)

	i = "hello"                    // 赋值 string 类型的 hello
	describe(i)
}

func describe(i interface{}) {
	fmt.Printf("(%v, %T)\n", i, i)
}

在需要处理多种类型数据的场景下,空接口会非常有用。比如 fmt.Print 函数,它可以接受多种类型的数据~

ok,这一章介绍了接口的值,以及两个有趣的“空”接口:nil interface 和 empty interface。下一章,继续探索接口~