概念:
接口即是一组行为的抽象与组合,即定义一些方法(行为)。在go中,interface是一种抽象类型,我们也可以称之为接口类型。
type Writer interface {
// 方法名(参数列表) 返回值列表
Write(p []byte) (n int, err error)
}
注意:
与其他编程语言中的接口不同,Go 中的interface是隐式实现的
type Spoker interface {
Say()
}
// siri
type Siri struct {}
// xiaoai
type XiaoAi struct {}
func (s Siri) Say() {
fmt.Println("你好,我是语音助手siri")
}
func (x XiaoAi) Say() {
fmt.Println("我是小爱,你的小米语音助手")
}
由上面的代码可知,go中并没显式地使用Spoker接口去实现方法,就实现了接口的所有方法。同时这种方式给我们带来略微的不便,不能清楚查看哪些struct实现了哪些接口,需要借助其他工具实现查看。
接口实现
1.条件:
- 一个struct 要实现这个接口就必须实现该接口的全部方法。
2.接口类型变量
接口类型变量存储所有实现了该接口的实例。
func main() {
// 声明Spoker类型的变量rx
var rx Spoker
// 实例化Siri
var s Siri = Siri{}
// 实例化XiaoAi
var x XiaoAi = XiaoAi{}
// 赋值Siri实例给变量rx
rx = s
rx.Say()
// 赋值XiaoAi实例给变量rx
rx = x
rx.Say()
}
面试题:
下面代码能否编译通过?
type People interface {
Speak(string) string
}
type Student struct {}
func (stu *Student) Speak(str string) (ctx string) {
if str == "xb" {
ctx = "学霸"
} else {
ctx = "学渣"
}
return
}
func main() {
pep := Student{}
ctx := "xz"
var content string = pep.Speak(ctx)
fmt.Println(content)
}
控制台输出:
// console
[Running] go run "/Users/sakurayang/unit_go/src/code.local/interface/interfaceTest.go"
学渣
[Done] exited with code=0 in 1.292 seconds
3.嵌入式接口
在go中, 接口还可以像👇的例子组装,不仅提高代码的简洁,还增强其灵活性。
package main
import "fmt"
type Eater interface {
Eat() string
}
type Humaner interface {
Eater
}
type Student struct {
Name string
}
func (s *Student) Eat() string {
return s.Name + " eating at the moment."
}
func main() {
stu := Student{"Lily"}
temp := stu.Eat()
fmt.Println(temp)
}
Go 语言底层大量包就是基于接口嵌套的方式实现的,其中举个广泛使用的接口 io 里面的 ReadWriter :
// Reader is the interface that wraps the basic Read method.
type Reader interface {
Read(p []byte) (n int, err error)
}
// Writer is the interface that wraps the basic Write method.
type Writer interface {
Write(p []byte) (n int, err error)
}
// ReadWriter is the interface that groups the basic Read and Write methods.
type ReadWriter interface {
Reader
Writer
}
interface类型
1.非空接口类型
通常我们实现接口时,既可以结构体类型的方法也可以是使用指针类型的方法。Go语言中并没有严格规定实现者的方法是值类型还是指针。
package main
import "fmt"
type Student struct {
Id int
Name string
Gender bool
}
type Beaner interface {
GetName() string
SetName(string)
}
func (s Student) GetName() string { // 1
return s.Name
}
func (s *Student) SetName(name string) { // 2
s.Name = name
}
func main() {
stu := Student{1, "lisi", true} // 3 类型:main.Student
stu.SetName("zhangsan")
fmt.Println(stu.GetName(), stu)
copyObj(stu)
}
// --- console ---
[Running] go run "/Users/sakurayang/unit_go/src/code.local/interface/getterInferface.go"
zhangsan {1 zhangsan true}
{1 wanger true}
[Done] exited with code=0 in 0.808 seconds
- 使用结构体实现接口
- 使用了结构体指针实现接口
- 使用结构体初始化变量(赋值触发类型的转换)
当我们如下面这样编写代码时,对比上面的栗子, 思考下面的例子为什么会出现编译失败?
func main() {
var stu Beaner = Student{1, "lisi", true} //
stu.SetName("zhangsan")
fmt.Println(stu.GetName(), stu)
}
[Running] go run "/Users/sakurayang/unit_go/src/code.local/interface/getterInferface.go"
# command-line-arguments
./getterInferface.go:3:6: cannot use Student{...} (type Student) as type Beaner in assignment:
Student does not implement Beaner (SetName method has pointer receiver)
[Done] exited with code=2 in 0.159 seconds
结论1: go中函数都是按值传递
结论2: 如果实现了接收者是值类型的方法,会隐含地也实现了接收者是指针类型的方法。
package main
import "fmt"
type Eater interface {
Eat()
Sleep()
}
type Humaner interface {
Eater
}
type Student struct {
Name string
}
func (s *Student) Eat() {
fmt.Println(s.Name + " eating at the moment.")
}
func (s *Student) Sleep() {
fmt.Println(s.Name + " go to bed at night.")
}
func main() {
var s Humaner = &Student{"Anna"}
s.Eat()
s.Sleep()
}
// ------- console -------
[Running] go run "/Users/sakurayang/unit_go/src/code.local/interface/nestedInterface.go"
Anna eating at the moment.
Anna go to bed at night.
[Done] exited with code=0 in 0.661 seconds
看一下下面这个栗子,思考为什么错误:
package main
import "fmt"
type Eater interface {
Eat()
Sleep()
}
type Humaner interface {
Eater
}
type Student struct {
Name string
}
func (s *Student) Eat() {
fmt.Println(s.Name + " eating at the moment.")
}
// func (s *Student) Sleep() {
// fmt.Println(s.Name + " go to bed at night.")
// }
func main() {
var s Humaner = &Student{"Anna"}
s.Eat()
//s.Sleep()
}
// ----- console ------
[Running] go run "/Users/sakurayang/unit_go/src/code.local/interface/nestedInterface.go"
# command-line-arguments
./nestedInterface.go:27:6: cannot use &Student{...} (type *Student) as type Humaner in assignment:
*Student does not implement Humaner (missing Sleep method)
[Done] exited with code=2 in 0.229 seconds
总结
- go中要实现该接口,必须实现该接口所有方法名的函数方法
2.interface{} 类型(空接口)
应用场景:
1.可以接收任何类型的函数参数
func main() {
n := "zhangsan"
a := 13
g := true
convertType(n)
convertType(a)
convertType(g)
}
func convertType(t interface{}) {
fmt.Printf("val:%v type:%T\n", t, t)
}
2.作为map的值,保存任意值的字典
m := make(map[string]interface{})
m["id"] = 1
m["name"] = "zhangsan"
m["gender"] = true
3.interface{} 类型不是任意类型
type TestObj struct{}
func checkNil(t interface{}) bool {
return t == nil
}
func main() {
var s *TestObj
fmt.Println(s == nil)
fmt.Println(checkNil(s))
}
类型断言
v, ok := x.(T)
x: interface{}的变量
T: 类型
返回参数:
1. v: x转T类型后的变量;
2. ok: 布尔值, 如果为true,则为断言成功; 如果为false, 则为断言失败。
下面举个栗子:
package main
import "fmt"
func checkType(x interface{}) {
switch v := x.(type) {
case string:
fmt.Printf("the type is a string, value is %v\n", v)
case int:
fmt.Printf("the type is a int, value is %v\n", v)
case bool:
fmt.Printf("the type is a bool, value is %v\n", v)
default:
fmt.Printf("the unknown type.")
}
}
func main() {
m := "zhangsan"
checkType(m)
n := 123456
checkType(n)
k := true
checkType(k)
}