接口定义与基本使用 - interface
go语言中,接口类型可以定义一组方法,不需要在接口定义中实现方法,并且interface中不能含有变量,如果某个自定义类型要使用时再实现接口的方法。
golang中的接口不需要显式地实现,只要一个类型实现了接口所有的方法,那么这个类型就实现了这个接口
接口的声明与实现
package main
import (
"fmt"
)
// 声明一个接口
type Usb interface {
// 声明两个没有实现的方法
Start()
Stop()
}
type Phone struct {
}
// 让Phone实现Usb接口的方法
// 实现一个接口,就是实现接口的所有方法
func (p Phone) Start() {
fmt.Println("phone start")
}
func (p Phone) Stop() {
fmt.Println("phone stop"))
}
type Camera struct {
}
func (c Camera) Start() {
fmt.Println("camera start")
}
func (c Camera) Stop() {
fmt.Println("camera stop")
}
type Computer {
}
// Working接收一个Usb接口的参数
func (c Computer) Working(usb Usb) {
usb.start()
usb.stop()
}
func main() {
computer := Computer{}
phone := Phone{}
camera := Camera{}
// 调用phone的方法
computer.Working(phone)
// 调用camera的方法
computer.Working(camera)
}
接口应用场景
定义好接口后,可方便地分配好各个接口的实现工作
接口使用的注意事项
- 接口不能用来创建对象,但可以用来指向一个实现了该接口的对象
type Student struct{
Name string
}
type A interface {
Say()
}
func (stu Student) Say() {
fmt.Println(stu.Name)
}
func main() {
stu := Student{"John"}
// 可以用接口指向一个定义好的对象,然后用接口调用对象的方法
a A = stu
a.Say()
}
- 只要是自定义类型,都可以实现接口
- 一个自定义类型可以实现多个接口。如果将一个类型变量赋值给不同的接口,那么就能使用不同的接口来调用该类型的方法。
- 一个接口可以继承自其他多个接口,这时如果要实现该接口,需要实现其继承的所有接口,继承的方式也类似结构体的继承,定义匿名接口即可,下例:
type Ai interface {
SayA()
}
type Bi interface {
SayB()
}
type Ci interface {
Ai
Bi
SayC()
}
- interface类型默认是指针,如果没初始化就使用,会输出nil
- 空接口没有实现任何方法,所有类型都实现了空接口,我们可以把任意类型(包括基础数据类型)的变量赋值给空接口;
type T interface{
}
func main() {
var stu Student
var t T = stu
}
- 接口的继承不允许出现相同的方法名
- 在将变量赋值给接口时,严格区分指针类型和值类型
type Cap interface{
Say()
}
type Stu struct{
}
func (this *Stu) Say() {
fmt.Println("stu say")
}
func main() {
var stu Stu = Stu{}
cap interface = &stu // 这里不能写成stu
}
接口的最佳实践案例
实现对hero结构体切片的排序,我们可以用官方库里面的Sort函数,这个函数接收Interface参数,只需要实现Interface的三个方法,即可对对应的接口变量进行排序:
package main
import(
"fmt"
"sort"
)
type Hero struct {
Name string
Age int
}
// 声明一个Hero切片类型
type HeroSlice []Hero
// 实现Interface的三个方法
func (hs HeroSlice) Len() int {
return len(hs)
}
// 若i下标变量大于j下标变量成立被返回,则降序,否则升序
func (hs HeroSlice) Less(i int, j int) bool {
return hs[i].Age > hs[j].Age
}
func (hs HeroSlice) Swap(i, j int) {
//temp := hs[i]
//hs[i] = hs[j]
//hs[j] = temp
// go中可以直接这样写交换
hs[i], hs[j] = hs[j], hs[i]
}
func main() {
var heros = HeroSlice
// 初始化heros...
// 排序
sort.Sort(heros)
}
接口与继承的对比总结
- 当我们不想破坏继承关系,但想对某个自定义类型扩展功能,那么就可以通过接口来实现,实现接口可以看做对继承的一种补充
- 当两个不同的结构体需要实现同样的方法的时候,用接口比在原结构体上分别实现方法会更加灵活
- 继承的价值更多在于复用和可维护,接口的价值在于规范设计,至于规范实现可交由其他自定义类型实现
go语言中的多态
所谓多态,指的是变量的多种形态,像c/c++语言中允许父类的指针指向子类的对象,在go里面,多态是通过接口来实现的,通过统一的接口来调用不同的实现,接口变量就呈现了多种形态。如Usb接口,可以接收手机变量和相机变量,就体现了usb接口的多态特征(根据传入的变量具体类型来决定调用什么方法)
接口的多态体现
- 多态参数,也即同一个接口作为参数,但可以根据具体传入的变量类型来决定使用的方法
- 多态数组,如定义一个Usb接口,可以存放phone结构体对象和Camera结构体对象,这就允许数组存放多种类型。