什么是接口
接口是有一组方法签名组成的类型定义,并表明任何实现了这些方法的类型实例都属于这个接口的实例。接口定义的一种类型系统,用于抽象和封装具体的实现,让代码具有更强的灵活性和可扩展性。
type interfaceName interface {
methodName(argType ReturnType) returnType
//...
}
例如,定义一个可以调用三种功能的smartDevice
接口:
type smartDevice interface {
playMusic(song string) error
setAlarm(time string) error
makeCall(number string) error
}
接口的实现是隐式的,只要一个类型实现了接口中的所有方法,那么它就实现了这个接口。例如,以下类型smartPhone
就实现了smartDevice
接口:
type smartPhone struct {
name string
}
func (p smartPhone) playMusic(song string) error {
// implement playMusic
return nil
}
func (p smartPhone) setAlarm(time string) error {
// implement setAlarm
return nil
}
func (p smartPhone) makeCall(number string) error {
// implement makeCall
return nil
}
在这个例子中,因为 smartPhone
类型实现了 smartDevice
接口中的所有方法,所以我们说 smartPhone
类型实现了 smartDevice
接口。
如何判断是否实现了某接口
if _, ok := x.(T); ok {
// x implements the interface T
} else {
// x doesn't implement the interface T
}
在这里,T可以是接口类型或非接口类型,如果T是非接口类型,则上述表达式检查x的动态类型是否为T。如果T是接口类型,则检查x的动态类型是否实现了接口T。如果检查成功,ok等于true且v的类型为T;否则ok等于false且v的类型为T的零值。
如何实现多接口的继承
Go语言中的类型(包括自定义类型和内置类型)可以实现多个接口。只要类型的方法满足了多个接口的所有方法,就可以被认为实现了这些接口。
// 定义了一个Worker接口,有Work方法
type Worker interface {
Work()
}
// 定义了一个Eater接口,有Eat方法
type Eater interface {
Eat()
}
// 定义了一个Rest接口,有Rest方法
type Rest interface {
Rest()
}
// 定义了一个Human结构体,有Name属性,Work、Eat、Rest方法
type Human struct {
Name string
}
func (h Human) Work() {
fmt.Println(h.Name, "is working.")
}
func (h Human) Eat() {
fmt.Println(h.Name, "is eating.")
}
func (h Human) Rest() {
fmt.Println(h.Name, "is resting.")
}
func mAIn() {
mike := Human{"Mike"}
// 这个地方可以看到,Human结构体实现了Work、Eat和Rest三个接口
var w Worker = mike
var e Eater = mike
var r Rest = mike
w.Work()
e.Eat()
r.Rest()
}
什么是指针和引用
举例:代码如下他们有什么区别
func (h Human) Rest() {
fmt.Println(h.Name, "is resting.")
}
和func (h *Human) Rest() {
fmt.Println(h.Name, "is resting.")
两种写法的区别在于接收者类型:(h Human)
是值接收者,(h *Human)
是指针接收者。
当使用指针接收者 (h *Human)
时,修改接收者内部的属性将会改变接收者本身,因为在函数内部我们对接收者的操作是对接收者地址进行操作的。
func (h *Human) setName(name string) {
h.Name = name // 这将会改变原对象的Name属性
}
而对于值接收者 (h Human)
,函数内部接收的是接收者的一个副本,即使修改也不会影响到原对象。
func (h Human) setName(name string) {
h.Name = name // 这不会改变原对象的Name属性,只修改副本
}
除此之外,值接收者在函数调用时,会对接收者进行一次拷贝,而指针接收者不会。所以,如果接收者是一个较大的结构体时,指针接收者会更为高效。而且,指针接收者也能够处理nil值。