三、接口也支持继承
结构体可以通过组合实现面向对象继承的特性,接口也可以通过组合实现继承。
定义 Mark1、Mark2 和 Mark44 三个接口,Mark44 除了拥有 Mark1 和 Mark2 的所有功能(方法)外,还有自己独特的功能。
type Mark1 interface { Flames() // 喷火 } type Mark2 interface { Fly() // 飞行 } type Mark44 interface { Mark1 Mark2 AntiHulk() } type IronMan struct { Address string } func (i IronMan) Flames(){ fmt.Println("喷火ing") } func (i IronMan) Fly(){ fmt.Println("飞行ing") } func (i IronMan) AntiHulk(){ fmt.Println("反浩克ing") } 复制代码
在 main 方法中声明一个 Mark44 接口类型的变量并赋值一个 IronMan 结构体的实例化对象。
func main(){ var mark44 Mark44 = IronMan{"Earth 616"} mark44.Fly() mark44.AntiHulk() } 复制代码
执行上述代码,输出结果如下:
飞行ing 反浩克ing 复制代码
当然组合结构体实现组合接口也是可行的,在上述代码中增加一个结构体 Mankind,并将该结构体放进 IronMan 结构体中
type Mankind struct { Name string } type IronMan struct { Mankind Address string } 复制代码
修改 main 方法
func main(){ man := Mankind{"tony"} var mark44 Mark44 = IronMan{man, "Earth 616"} mark44.Fly() mark44.AntiHulk() } 复制代码
再次调用 main 方法,输出结果如下:
飞行ing 反浩克ing 复制代码
四、空接口的应用场景
Go 中允许接口中不包含任何方法,既允许空接口的存在,空接口可以直接定义;
type 接口名 interface { } 复制代码
也可以通过变量声明一个空接口
var 变量名 interface{} 复制代码
空接口变量可以被赋值任何类型的数据。
空接口作为 Map 的值
在定义 Map 的时候通常都需要指定 Map 的键和值的类型,也就是说 Map 中值的类型是固定的,但是如果使用空接口作为值的类型的话,则值可以为任意类型。
func main(){ info := make(map[string]interface{}) info["name"] = "stark" info["age"] = 33 info["suit"] = []string{"Mark2", "Mark3", "Mark44", "Mark57"} info["balance"] = 3.14 fmt.Println(info) } 复制代码
执行上述代码,输出结果如下:
map[age:33 balance:3.14 name:stark suit:[Mark2 Mark3 Mark44 Mark57]] 复制代码
空接口作为函数参数
前面提到空接口类型变量可以接收任意类型的数据,那么将空接口作为函数的参数之后,函数的参数也将不受类型的限制。
新增一个 output 函数,以空接口作为参数
func output(i interface{}){ fmt.Printf("%v\n", i) } 复制代码
在 main 函数中新增如下代码
func main(){ // 原代码保持不变 output(info) output(info["suit"]) output(info["name"]) } 复制代码
执行上述代码,输出结果如下:
map[age:33 balance:3.14 name:stark suit:[Mark2 Mark3 Mark44 Mark57]] [Mark2 Mark3 Mark44 Mark57] stark 复制代码
五、Go 的 error 接口
Go 中的 error 类型也是一个接口,该接口包含了一个 Error() 方法,返回一个 string 字符串
可以使用结构体来实现 error 接口,用作自定义的业务错误类型
func main() { var err error = LoginError{"用户名或密码错误", 00001} if err != nil { fmt.Println(err) } else { fmt.Println("登录成功") } } type LoginError struct { Message string Code int } func (err LoginError) Error() string{ return err.Message } 复制代码
执行上述代码,输出结果如下:
用户名或密码错误 复制代码
除了实现 error 接口外还可以导入 errors
标准库,通过 errors.New("错误信息")
实例化的方式来给 err 接口变量赋值
import ( // 导入 errors 包 "errors" "fmt" ) func main() { var err error = errors.New("用户名密码错误") // 其余代码保持不变 } 复制代码
但是最常用的其实是第三种方式,既通过 fmt
包下的 Errorf
函数的返回来给 error 接口变量赋值。
Errorf
函数的返回值是一个 error 接口类型
也可以通过这种方式给 error 接口变量赋值
var err error = fmt.Errorf("%v", "用户名或者密码错误")