一、感受接口
type Usb interface { Connect() Disconnect() } // 手机 type Phone struct {} // 相机 type Camera struct {} // 计算机 type Computer struct {} // 手机实现接口所有方法 func (p Phone) Connect() { fmt.Println("手机连接中...") } func (p Phone) Disconnect() { fmt.Println("手机断开连接中...") } // 相机实现接口所有方法 func (c Camera) Connect() { fmt.Println("相机连接中...") } func (c Camera) Disconnect() { fmt.Println("相机断开连接中...") } // Working方法,接收一个Usb接口类型变量 func (c Computer) Working(usb Usb) { // 通过接口变量来调用Connect和Disconnect方法 usb.Connect() usb.Disconnect() } func main() { // 创建结构体实例 phone := Phone{} camera := Camera{} computer := Computer{} // 关键点 computer.Working(phone) computer.Working(camera) } // 输出如下 // 手机连接中... // 手机断开连接中... // 相机连接中... // 相机断开连接中...
当传入一个phone,usb就能识别是手机,传入camera,usb就能识别是相机,并别分调用其相应的方法。这很明显就是多态呀!!!
func (c Computer) Working(usb Usb) { usb.Connect() usb.Disconnect() } func main() { phone := Phone{} camera := Camera{} computer := Computer{} computer.Working(phone) computer.Working(camera) }
二、接口介绍
interface 类型可以定义一组方法,但是这些方法不需要实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现了这个接口。
语法:
type 接口名 interface { method1(参数列表) 返回值列表 method2(参数列表) 返回值列表 ... }
三、注意事项和细节
- 接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的实例;
- 接口中所有的方法都没有方法体,即只定义方法,没有实现该方法;
- 一个自定义类型只有实现了某个接口,才能将该自定义类型的实例赋给该接口类型;
- 只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型;
- 一个自定义类型可以实现多个接口;
- golang的接口不能有任何变量;
- 一个接口(比如 C 接口)可以继承多个别的接口(比如 A、B 接口),这时如果要实现 C 接口,也必须将 A、B 接口中的方法也实现;注意,A、B接口中不能有相同的方法,相当于 C 接口有两个相同的方法,这是不允许的;
- interface 类型默认是一个指针(引用类型),如果没有对 interface 初始化就使用,那么会输出 nil ;
- 空接口 interface{} 没有任何方法,因此所有类型都实现了空接口,即可以把任何一个变量赋给空接口;
- 如果一个自定义类型是使用指针方式实现一个接口的,那么需要将该自定义类型的地址赋给该接口,不然会报错。
type AInterface interface { Eat() } type Person struct { Name string } func (p Person) Eat() { fmt.Printf("%s正在吃饭...\n", p.Name) // 佩奇正在吃饭... } func main() { p := Person{"佩奇"} var a AInterface = p // 接口可以指向一个实现了该接口的自定义类型实例 a.Eat() }
type AInterface interface { Eat() } type BInterface interface { Sleep() } type Person struct { Name string } func (p Person) Eat() { fmt.Printf("%s正在吃饭...\n", p.Name) // 佩奇正在吃饭... } func (p Person) Sleep() { fmt.Printf("%s正在睡觉...\n", p.Name) // 佩奇正在睡觉... } func main() { p := Person{"佩奇"} var a AInterface = p var b BInterface = p a.Eat() b.Sleep() }
type AInterface interface { Eat() } type BInterface interface { Sleep() } type CInterface interface { AInterface BInterface Study() } type Person struct { Name string } func (p Person) Eat() { fmt.Printf("%s正在吃饭...\n", p.Name) // 佩奇正在吃饭... } func (p Person) Sleep() { fmt.Printf("%s正在睡觉...\n", p.Name) // 佩奇正在吃饭... } func (p Person) Study() { fmt.Printf("%s正在学习...\n", p.Name) // 佩奇正在学习... } func main() { p := Person{"佩奇"} var c CInterface = p c.Eat() c.Sleep() c.Study() }
type T interface {} type Integer int func main() { var int1 Integer int2 := Integer(100) var t T t = int1 fmt.Println(t) // 0 t = int2 fmt.Println(t) // 100 }
type A interface { Eat() } type Person struct { Name string } // 使用 Person 指针类型实现一个接口的方法 func (p *Person) Eat() { fmt.Printf("%s正在吃饭...", p.Name) } func main() { p := Person{"佩奇"} //var a A = p // 错误!!!因为Person类型没有实现A接口,修改如下 var a A = &p a.Eat() }
四、接口最佳实践
实现对 Person 结构体切片的排序:sort.Sort(data Interface)
package main import ( "fmt" "math/rand" "sort" "time" ) type Person struct { Name string Age int } type PersonSlice []Person func (ps PersonSlice) Len() int { return len(ps) } // Less方法决定使用什么标准进行排序 // 这里按年龄从小到大排序 func (ps PersonSlice) Less(i, j int) bool { return ps[i].Age < ps[j].Age } func (ps PersonSlice) Swap(i, j int) { ps[i], ps[j] = ps[j], ps[i] } func main() { var ps PersonSlice rand.Seed(time.Now().UnixNano()) for i := 0; i < 10; i++ { person := Person{ Name: fmt.Sprintf("无名%d号", i), Age: rand.Intn(100), } ps = append(ps, person) } // 排序前的顺序 for _, v := range ps { fmt.Println(v) } // 调用sort.Sort方法 sort.Sort(ps) fmt.Println() // 排序后的顺序 for _, v := range ps { fmt.Println(v) } }
输出结果:
2