对初学者讲,理解接口的概念不算太难,难的是不知道什么时候使用接口,下面我例举几个应用场景:
1.说现在美国要制造轰炸机,武装直升机,专家只需把飞机需要的功能/规格定下来即可,然后让别的人具体实现就可。
要去写一个结构体,结构体里面有些方法·,这些方法让一个程序员去写肯定是不太好的,希望将每个人都动起来。可以找项目经理,项目经理去定义一个接口,大家去实现这个接口就完事了。这样就可以很好的控制管理项目的进度。
其实也就是取定义规范,其他人将规范实现即可。
你想要去排序,只需要取实现这个data接口就行了。
其实也就是传入实现了interface接口的方法全部实现的,那么就可以去调用包里面的sort方法,将这个类型传入进去即可。
sort包里面有一个Sort函数,sort里面接受data,它是一个接口。那么只要传进去的变量实现了接口Interface里面所有方法,传入进去,它自然就给你排序了。当你实现接口,那么Sort函数就会自动帮你实现排序。
如果如下所示,之前使用email发送告警信息,后面如果要换成dingding去发送告警,那么代码就需要改动。
//如果产生了一个告警,将告警发送给相关的人
//email sms weixin
type EmailSender struct {
}
type DingDingSender struct {
}
func (e *EmailSender) Send(msg string) error {
fmt.Println(msg)
return nil
}
func (d *DingDingSender) Send(msg string) error{
fmt.Println(msg)
return nil
}
func test(e *EmailSender,msg string) {
e.Send(msg)
}
func main() {
sender := new(EmailSender)
sender.Send("email send")
}
如果需要改动得修改如下,要将和email相关的代码全部修改,如果没有全部修改会导致编译的时候失败。
为了解决这种问题,可以定义接口,这些结构体有个特点就是都有Send方法,这样就可以定义只有Send方法接口,那么参数类型就可以使用接口类型。
如果定义了接口就方便多了,只需要改一个地方,也就是结构体实例化的地方即可
type Sender interface {
Send(string) error
}
sender := new(DingDingSender)
test(sender,"dingding send")
sender1 := new(EmailSender)
test(sender1,"email send")
2.说现在有一个项目经理,管理三个程序员,功能开发一个软件,为了控制和管理软件,项目经理可以定义一些接口,然后由程序员具体实现。
项目经理只需要将接口定义好,剩下的让程序员a b c共同完成。这样就可以控制软件的开发进度。
1)接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)
type interfaceA interface {
Say()
}
type Student struct {
}
func (*Student) Say() {
fmt.Println("I am a student")
}
func main() {
var i interfaceA
student := new(Student)
i = student //结构体变量实现了接口的方法
i.Say()
}
2)接口中所有的方法都没有方法体,即都是没有实现的方法。
3)在Golang中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口。
4)一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型。
5)只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型。
type interfaceA interface {
Say()
}
type integer int
func (i *integer) Say() {
fmt.Println("i地址为:", &i, "i的值为:", *i)
}
func main() {
var i interfaceA
var a integer = 5
a.Say()
}
6)一个自定义类型可以实现多个接口
7)Golang接口中不能有任何变量
上面可以看到和传统的oop不一样,他是基于方法来实现的这个接口。而java这些它是显示实现的,必须显示的指定去实现哪个接口。而golang并不关心实现了哪个接口,只关心实现了哪个方法。
8)一个接口(比如 A接口)可以继承多个别的接口(比如 B,C接口),这时如果要实现A接口,也必须将B,C接口的方法也全部实现。
可以看到A接口里面有两个方法,相当于将这两个接口继承下来了。那么就要将继承下来的接口里面的方法和本身的方法都去实现。
9) interface类型默认是一个指针(引用类型),如果没有对interface初始化就使用,那么会输出nil(在传入参数的时候,比如结构体,传入的不是值类型,传入的是引用类型,也就是&struct{})
10)空接口interface没有任何方法,所以所有类型都实现了空接口(空接口其实就是一种数据类型,可以将任何的数据类型的变量赋值给空接口,如果参数是空接口的形参,那么意味着可以接受任何一种数据类型)
下面可以看到结构体默认实现了空接口,其他类型也可以,字符串整型这些都是可以的。
type T interface {
}
type student struct {
age int
name string
}
func main() {
s := &student{
age: 10,
name: "lucas",
}
var t T = s
fmt.Println(reflect.TypeOf(t))
fmt.Println(t)
var t3 interface{} = s
fmt.Println(t3)
}
*main.student
&{10 lucas}
&{10 lucas}
可以将任何的变量赋值给空接口