1.简介
- 在go中的接口是以一种类型,一种抽象的类型。接口(interface)是一组函数method的集合,go中的接口不能包含任何变量。
- 在go中接口中的所有方法都没有方法体,接口定义了一个对象的行为规范,只定义规范不实现。接口体现了程序的多态和高内聚低耦合的思想。
- go中的接口也是一种数据类型,不需要显示实现,只需要一个变量含有接口类型中的所有方法,那么这个变量就实现了这个接口。
2.接口的定义
interface类型可以定义一组方法,但是这些不需要实现。并且interface不能包含任何变量。到某个自定义类型要使用的时候,再根据具体情况把这些方法写出来(实现)。
type 接口名 interface {
method1(参数...)返回值列表
method2(参数...)返回值列表
}
实现:
func (t 自定义类型) method1(参数...)返回值列表{
//方法体
}
func (t 自定义类型) method2(参数...)返回值列表{
//方法体
}
其中:
- 接口名:使用type将接口定义为自定义类型名,go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口writer,有字符串功能的接口叫Stringer等。接口名最好能突出该接口的类型含义。
- method:当方法名首字母是大写且这个接口名首字母也是大写,这个方法可以被接口所在的包(package)之外的代码访问。
- 参数和返回值列表:参数和返回值列表中的参数名可以省略。
3.快速入门
定义一个Usber接口让Phone和Camera结构体实现这个接口。
package main
import "fmt"
// 定义一个接口
type Usber interface {
//声明了两个没有实现的方法
start()
stop()
}
type Phone struct {
}
// 手机要实现usb接口的化必须得实现usb接口中的所有方法
func (p Phone) start() {
fmt.Println("手机开始工作了")
}
func (p Phone) stop() {
fmt.Println("手机停止工作了")
}
type Camera struct {
}
// 照相机要实现usb接口的化必须得实现usb接口中的所有方法
func (p Camera) start() {
fmt.Println("照相机开始工作了")
}
func (p Camera) stop() {
fmt.Println("照相机停止工作了")
}
type Computer struct {
}
// 编写一个方法Working,接口一个Usber接口类型的变量
// 只要实现了Usber接口,
func (c Computer) Working(usb Usber) {
//通过usb接口变量来调用start和stop方法
usb.start()
usb.stop()
}
func main() {
var c *Computer = &Computer{}
var u Usber
u = &Phone{}
c.Working(u) //相当于 手机调用了start()和stop()
u = Camera{}
c.Working(u) //相当于 照相机调用了start()和stop()
}
结果:
手机开始工作了
手机停止工作了
照相机开始工作了
照相机停止工作了
4.空接口
4.1快速入门
go中的接口可以不定义任何方法,没有定义任何方法的接口就是空接口。空接口表示没有任何约束,因此任何类型变量都可以实现空接口。
空接口在实际项目中用的非常多,用空接口可以表示任意数据类型。
package main
import "fmt"
func main() {
// 定义一个空接口x,x变量可以接收任意的数据类型
var x interface{}
s := "我是字符串"
x = s
fmt.Printf("type:%T value:%v\n", x, x)
num := 2
x = num
fmt.Printf("type:%T value:%v\n", x, x)
b := true
x = b
fmt.Printf("type:%T value:%v\n", x, x)
}
结果:
type:string value:我是字符串
type:int value:2
type:bool value:tru
4.2空接口作为函数的参数
package main
import "fmt"
func show(a ...interface{}) {
for _, value := range a {
fmt.Printf("type:%T value:%v\n", value, value)
}
}
func main() {
show("我是字符串!", 123, false, []int{1, 2, 3})
}
结果:
type:string value:我是字符串!
type:int value:123
type:bool value:false
type:[]int value:[1 2 3]
4.3map的值实现空接口
使用空接口可以实现保存任意值得map。
map1 := make(map[string]interface{})
map1["name"] = "张三"
map1["age"] = 12
map1["flag"] = false
fmt.Println(map1)//map[age:12 flag:false name:张三]
4.4切片实现空接口
slice1 := make([]interface{}, 0)
slice1 = append(slice1, 12)
slice1 = append(slice1, "name")
slice1 = append(slice1, true)
fmt.Println(slice1) //[12 name true]
5.结构体值接受者和指针接受者实现接口的区别
5.1值接收者
如果结构体实现的方法是值接收者,那么实例化后的结构体值类型和结构体指针类型都可以赋值给接口变量。
package main
import "fmt"
type Usb interface {
start()
stop()
}
type Phone struct {
Name string
}
func (receiver Phone) start() {
fmt.Println(receiver.Name)
}
func (receiver Phone) stop() {
fmt.Println(receiver.Name)
}
func main() {
var usb Usb
usb = Phone{
Name: "我是值接收者类型",
}
usb.start() //我是值接收者类型
usb.stop() //我是值接收者类型
usb = &Phone{
Name: "我是指针接收者类型",
}
usb.start() //我是指针接收者类型
usb.stop() //我是指针接收者类型
}
5.2指针类型接收者
如果结构体实现的方法是指针接受者,那么实例化后结构体指针类型都可以赋值给接口变量,结构体值类型不能赋值给接口变量。
注意:只要有一个结构体实现的方法是指针接收者,都是指针类型接收者。
package main
import "fmt"
type Usb interface {
start()
stop()
}
type Phone struct {
Name string
}
func (receiver *Phone) start() {
fmt.Println(receiver.Name)
}
func (receiver Phone) stop() {
fmt.Println(receiver.Name)
}
func main() {
var usb Usb
//annot use Phone{…} (value of type Phone) as Usb value in assignment: Phone does not implement Usb (method start has pointer receiver)
//usb = Phone{
// Name: "我是值接收者类型",
//}
usb = &Phone{
Name: "我是指针接收者类型",
}
usb.start() //我是指针接收者类型
usb.stop() //我是指针接收者类型
}
6.一个结构体实现多个接口
package main
import "fmt"
type AInterface interface {
setName(string)
}
type BInterface interface {
getName() string
}
type Phone struct {
Name string
}
func (p *Phone) setName(name string) {
p.Name = name
}
func (p Phone) getName() string {
return fmt.Sprint("我的型号是", p.Name)
}
func main() {
var a AInterface
var b BInterface
p := &Phone{
Name: "apple",
}
a = p
b = p
fmt.Printf("a的类型:%T b的类型:%T\n", a, b)
fmt.Println(b.getName())
a.setName("vivo")
fmt.Println(b.getName())
}
结果:
a的类型:*main.Phone b的类型:*main.Phone
我的型号是apple
我的型号是vivo
7.接口的嵌套
接口与接口间可以通过嵌套创造出新的接口。也称接口的继承。
package main
import "fmt"
type AInterface interface {
testA()
}
type BInterface interface {
testB()
}
// 如果需要实现CInterface,就需要将AInterface,BInterface的方法都实现
type CInterface interface {
AInterface
BInterface
testC()
}
type DStruct struct {
}
func (s DStruct) testA() {
fmt.Println("testA")
}
func (s DStruct) testB() {
fmt.Println("testB")
}
func (s DStruct) testC() {
fmt.Println("testC")
}
/**
CInterface继承了AInterface和BInterface,这时要实现CInterface,也必须将AInterface和BInterface的方法也全部实现。
*/
func main() {
var a AInterface
var b BInterface
var c CInterface
ds := DStruct{}
a = ds
a.testA() //testA
b = ds
b.testB() //testB
c = ds
c.testA() //testA
c.testB() //testB
c.testC() //testC
}
8.注意事项和细节
8.1接口本身不能创建实例
接口本身不能创建实例,但是可以只想一个实现了该接口的自定义类型的变量(实例)。
package main
import "fmt"
type AInterface interface {
say()
}
type Student struct {
}
func (s Student) say() {
fmt.Println("学生 say")
}
func main() {
stu := Student{}
var a AInterface = stu
a.say() //学生 say
}
8.2接口中所有的方法都没有方法体,即都是没有实现的方法。
8.3接口实现与赋值
一个自定义类型需要将某个接口的所有方法都是先,我们说这个自定类型实现了该接口。
一个自定义类型只有实现某个接口,才能将该自定义类型的实例赋给接口普类型。
8.4只要时自定义数据类型,就可以实现接口
只要时自定义数据类型,就可以实现接口,不仅仅是结构体类型。
package main
import "fmt"
type AInterface interface {
say()
}
type MyInt int
func (m MyInt) say() {
fmt.Println("MyInt say")
}
func main() {
var myInt MyInt = 12
var a AInterface = myInt
a.say() //MyInt say
}
8.5一个自定义类型可以实现多个接口
8.6go接口不能有任何变量
8.7一个接口可以继承多个别的接口
一个接口A,可以继承多个别的接口比如B,C,这时如果要实现A接口,也必须将B,C接口的方法全部实现。
8.8interface类型默认是一个指针(引用类型)
如果没有对interface初始化就是用,那么会输出nil。
package main
import "fmt"
type AInterface interface {
say()
}
func main() {
var b AInterface
fmt.Println(b) //<nil>
fmt.Println(b == nil) //true
}
8.9空接口interface{}
空接口没有任何方法,所以所有类型都实现了空接口,即我们可以把任何一个变量赋值给空接口。
package main
import "fmt"
type AInterface interface {
}
func main() {
var b AInterface
b = 2
fmt.Println(b) //2
b = "hello"
fmt.Println(b) //hello
var e interface{}
e = 12
fmt.Println(e) //12
e = "word"
fmt.Println(e) //word
}
9.排序案例
实现对Student结构体切片的排序:sort.Sort(data Interface)
Interface接口
type Interface interface {
Len() int //获取长度
Less(i, j int) bool //决定你使用什么标准进行排序
Swap(i, j int) //数据交换
}
要想用sort.Sort( stuSilce []Student)进行排序,[]Student就需要实现Interface接口的三个方法。
package main
import (
"fmt"
"sort"
)
type Student struct {
Name string
Age int
Score float32
}
type StuSlice []Student
func (s StuSlice) Len() int {
return len(s)
}
// 按照成绩由高到底排序
func (s StuSlice) Less(i, j int) bool {
return s[i].Score > s[j].Score
}
func (s StuSlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func main() {
var stu StuSlice
stu = make(StuSlice, 5)
stu[0] = Student{
Name: "伊一",
Age: 10,
Score: 88,
}
stu[1] = Student{
Name: "王二",
Age: 11,
Score: 98,
}
stu[2] = Student{
Name: "刘三",
Age: 9,
Score: 89,
}
stu[3] = Student{
Name: "张三",
Age: 10,
Score: 98,
}
stu[4] = Student{
Name: "王五",
Age: 10,
Score: 99,
}
fmt.Println(stu)//[{伊一 10 88} {王二 11 98} {刘三 9 89} {张三 10 98} {王五 10 99}]
sort.Sort(stu)
fmt.Println(stu) //[{王五 10 99} {王二 11 98} {张三 10 98} {刘三 9 89} {伊一 10 88}]
}