接口(interface)
在Golang中,多态特性主要通过接口来体现的。
- 为什么要有接口呢?
- 是代码的耦合性降低,通用性增强(在现实生活中,USB就是一个接口)
- 快速入门
package main
import "fmt"
//声明一个接口
type Usb interface{
//声明了两个没有实现的方法
Start()
Stop()
}
//手机
type Phone struct{
}
//让结构体实现Usb接口的方法
func(phone Phone)Start(){
fmt.Println("手机开始工作")
}
func(phone Phone)Stop(){
fmt.Println("手机停止工作")
}
//相机
type Camera struct{
}
//让相机camera也实现Usb接口的方法
func(camera Camera)Start(){
fmt.Println("相机开始工作")
}
func(camera Camera)Stop(){
fmt.Println("相机停止工作")
}
//计算机
type Computer struct{
}
//编写一个方法Working 方法,接收一个Usb接口类型变量
//只要实现了Usb接口(所谓实现Usb接口,就是指实现了 Usb接口声明所有方法)
func (computer Computer)Working(usb Usb){
//通过usb接口变量来调用Start和Stop方法
usb.Start()
usb.Stop()
}
func main(){
//测试
//先创建结构体变量
c := Computer{}
phone := Phone{}
camera := Camera{}
//关键点
c.Working(phone)
c.Working(camera)
}
基本介绍
- interface类型可以定义一组方法,但是这些不需要实现。并且interface不能包含任何变量。
- 到某个自定义类型(比如结构体Phnoe)要使用的时候,在根据具体情况把这些方法写出来(实现)。
基本语法
- 接口里面所有方法都没有方法体。
- 接口体现了程序设计的多态和高内聚低耦合的思想
- Golang中的接口,不需要显示的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现这个接口。因此,Golang中没有implement这样的关键字
应用场景
-
某国要制造轰炸机、武装直升机。专家只需要把飞机需要的功能/规格定下来即可,然后让别的人具体实现就行。(功能相当于接口中未实现的方法)
-
现在有一个项目经理,管理三个程序员,开发一个软件,为了控制和管理软件,项目经理可以定义一些接口,然后由程序员具体实现。(项目经理定接口规范,程序员实现接口)
接口注意事项
- 接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)
- 只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型
- 一个自定义类型可以实现多个接口
- Golang接口中不能有任何变量
- 一个接口(比如A接口)可以继承多个别的接口(比如B、C接口),这时如果要实现A接口,也必须将B、C接口的方法也全部实现
package main
import "fmt"
//声明接口
type AInterface interface{
test01()
}
type BInterface interface{
test02()
}
type CInterface interface{
test03()
AInterface
BInterface
}
type Student struct{
}
//student结构体实现CInterface,则必须实现test03()、接口AInterface、BInterface中的方法
func (student Student)test01(){
fmt.Println("student实现AInterface接口中的方法test01()")
}
func (student Student)test02(){
fmt.Println("student实现BInterface接口中的方法test02()")
}
func (student Student)test03(){
fmt.Println("student实现CInterface接口中的方法test03()")
}
func main(){
var student Student
var a CInterface = student
a.test01()
a.test02()
a.test03()
}
- interface 类型默认是一个指针(引用类型),如果没有对interface初始化就使用,那么会输出nil
- 空接口interface{} 没有任何方法,所以所有类型都实现了空接口,即我们可以把任何一个变量赋给空接口
练习
- 下面的代码,左边的没有错误;右边的会报重复定义
- 下面代码第 5 行是错误代码,接口(Say()方法)是Stu指针实现的,不是Stu实现的。
接口编程最佳实践
- 实现对Hero结构体切片的排序:sort.Sort(data Interface)
//sort.Sort(data Interface)里面的interface有三个方法
package main
import (
"fmt"
"sort"
"math/rand"
)
//声明Hero结构体
type Hero struct{
Name string
Age int
}
//声明Hero结构体的切片类型
type HeroSlice []Hero
//实现Interface接口
func (hs HeroSlice)Len()int{
return len(hs)
}
//Less方法就是决定你使用什么标准进行排序
//1.按照Hero年龄从小到大排序
func (hs HeroSlice)Less(i, j int)bool{
return hs[i].Age < hs[j].Age
//修改成对Name的排序
//只需改 return hs[i].Name < hs[j].Name
}
func (hs HeroSlice)Swap(i, j int){
//交换
temp := hs[i]
hs[i] = hs[j]
hs[j] = temp
//简洁: hs[i], hs[j] = hs[j], hs[i] (等价于上面三句)
}
func main(){
//先定义一个数组/切片
var intslice = []int{0, -1, 10, 7, 90}
//要求对 intslice切片进行排序
//1、冒泡排序
//2、也可以使用系统提供的方法
sort.Ints(intslice)
fmt.Println(intslice)
//请大家对结构体切片进行排序
//测试,是否可以对结构体切片进行排序
var heros HeroSlice
for i := 0; i < 10; i++{
hero := Hero{
Name : fmt.Sprintf("英雄~%d", rand.Intn(100)),
Age : rand.Intn(100),
}
//将hero append 到heroes切片
heros = append(heros, hero)
}
//排序前的顺序
for _, v := range heros{
fmt.Println(v)
}
//调用sort.Sort方法
sort.Sort(heros)
//排序后的顺序
for _, v := range heros{
fmt.Println("排序后的顺序", v)
}
}
- 将student的切片,按score从大到小排序