1.基本介绍
接口( interface ) ,在 Golang 中多态特性主要是通过接口来体现的
2.快速入门
package main
import(
"fmt"
)
// 声明一个接口
type Usb interface {
//声明了两个没有实现的方法
Start()
Stop()
}
//声明一个结构体,实现Usb方法
type Phone struct{
}
func (p Phone) Start() {
fmt.Println("手机开始工作...")
}
func (p Phone) Stop() {
fmt.Println("手机停止工作...")
}
//相机实现Usb方法
type Camera struct{
}
func (c Camera) Start() {
fmt.Println("相机开始工作...")
}
func (c Camera) Stop() {
fmt.Println("相机停止工作...")
}
//计算机
type Computer struct{
}
//编写一个方法Working, 接收一个Usb接口类型变量
//实现了Usb接口(就是指实现了Usb接口声明的所有方法)
func (c Computer) Working(usb Usb) {
//通过Usb接口变量来调用Start,Stop方法
usb.Start()
usb.Stop()
}
func main() {
//先创建结构体变量
computer := Computer{}
phone := Phone{}
camera := Camera{}
//关键点
computer.Working(phone)
computer.Working(camera)
}
手机开始工作...
手机停止工作...
相机开始工作...
相机停止工作...
接口概念的再说明
interface 类型可以定义一组方法,但是这些不需要实现,并且 interfaoe 不能包含任何变量,到某个自定义类型(比如结构体 Phone )要使用的时候,再根据具体情况把这些方法写出来(实现)
3.基本语法
type 接口名 interface {
method1(参数列表) 返回值列表
method2(参数列表) 返回值列表
}
实现接口的方法:
function (t 自定义类型) method1(参数列表) 返回值列表{
//方法实现
}
function (t 自定义类型) method2(参数列表) 返回值列表{
//方法实现
}
小节说明
(1).接口里的所有方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的多态和高内聚低偶合的思想
(2 ). go中的接口,不需要显式的实现,只要一个变量,含有接口类型中的所有方法,那么这个变量就实现这个接口。因此, Golang 中没有implement 这样的关键字
应用场景
4.接口的注意事项和细节
(1).接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)
package main
import(
"fmt"
)
type AInterface interface{
Say()
}
type Student struct{
Name string
}
func (stu Student) Say(){
fmt.Println("Stu Say()")
}
func main() {
var stu Student //结构体变量,实现了Say(),也就实现了AInterface接口
var a AInterface = stu
a.Say()
}
(2).接口中的所有的方法都没有方法体,即都没有实现的方法
(3).在go中,一个自定义类型需要将某个接口的所有方法都实现,才会说这个自定义类型实现了该接口
(4).一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型
(5 ).只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型
package main
import(
"fmt"
)
type integer int
func (i integer) Say() {
fmt.Println("i integer, i=", i)
}
func main() {
var i integer = 10
var b AInterface = i
b.Say()//i integer, i= 10
}
(6).一个自定义类型可以实现多个接口
package main
import(
"fmt"
)
type AInterface interface{
Say()
}
type BInterface interface{
Hello()
}
type Monster struct{
}
func (m Monster) Say() {
fmt.Println("Monster Say()")
}
func (m Monster) Hello() {
fmt.Println("Monster Hello()")
}
func main() {
//Monster实现了AInterface,BInterface
var monster Monster
var a1 AInterface = monster
var b1 BInterface = monster
a1.Say()
b1.Hello()
}
(7).go接口不能有任何变量
(8).一个接口(比如A接口 )可以继承多个别的接口(比如 B , C 接口),这时如果要实现A接口,也必须将 B ,C 接口的方法也全部实现
package main
import(
"fmt"
)
type BInterface interface{
test01()
}
type CInterface interface{
test02()
}
type AInterface interface{
BInterface
CInterface
test03()
}
//如果需要实现AInterface,就需要把BInterface,CInterface额方法都实现
type Stu struct{
}
func (stu Stu) test01() {
}
func (stu Stu) test02() {
}
func (stu Stu) test03() {
}
func main() {
var stu Stu
var a AInterface = stu
a.test01()
}
(9).interface 类型默认是一个指针,如果没有对interface 初始化就使用,那么会输出nil
(10).空接口interface{}没有任何方法,所有所有类型都实现了空接口
package main
import(
"fmt"
)
//空接口
type T interface{
}
func main() {
var t T = stu //ok
fmt.Println(t) //{}
var t2 interface{} = stu
fmt.Println(t2) //{}
var num float64 = 2.33
t2 = num
fmt.Println(t2)// 2.33
}
5.接口案例
实现对Hero结构体切片的排序:sort.Sort(data.Interface)
package main
import (
"fmt"
"sort"
"math/rand"
)
//实现对Hero结构体切片的排序:sort.Sort(data interface)
//1.声明一个Here结构体
type Hero struct {
Name string
Age int
}
//2.声明一个Here结构体切片
type HeroSlice []Hero
//3.实现接口Interface接口
// func Sort(data Interface)
/*
type Interface interface {
// Len方法返回集合中的元素个数
Len() int
// Less方法报告索引i的元素是否比索引j的元素小
Less(i, j int) bool
// Swap方法交换索引i和j的两个元素
Swap(i, j int)
}
*/
func (hs HeroSlice) Len() int {
return len(hs)
}
//Less就是决定你使用什么标准进行排序
//1. 按照Here的年龄从小到大排序
//2.修改成对名字进行排序
func (hs HeroSlice) Less(i, j int) bool {
// 1. 按照Here的年龄从小到大排序
// return hs[i].Age < hs[j].Age //升序
// 2.修改成对名字进行排序
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{12, -1, 4, 2, 15}
//要求对intSlice切片进行排序
//1.冒泡排序
//2.使用系统提供的方法
sort.Ints(intSlice)
fmt.Println(intSlice)
//对结构体进行排序
//1.冒泡排序
//2.也可以使用系统提供的方法
//对结构体切片进行排序
var heroes HeroSlice
for i := 0; i < 10; i++ {
hero := Hero {
Name : fmt.Sprintf("英雄%d", rand.Intn(100)),
Age : rand.Intn(100),
}
//将hero append 到heroes切片
heroes = append(heroes, hero)
}
//排序前的顺序
for _, v := range heroes {
fmt.Println(v)
}
//调用sort.Sort
sort.Sort(heroes)
//排序后的顺序
fmt.Println("排序后")
for _, v := range heroes {
fmt.Println(v)
}
}
实现对Student结构体切片按照成绩升序进行排序
package main
import (
"fmt"
"math/rand"
"sort"
)
//实现对Student结构体切片按照成绩升序进行排序
//1.声明一个Student结构体
type Student struct{
Name string
Age int
Score float64
}
//2.声明一个Student切片
type StuSlice []Student
//3.实现接口Interface接口
// func Sort(data Interface)
/*
type Interface interface {
// Len方法返回集合中的元素个数
Len() int
// Less方法报告索引i的元素是否比索引j的元素小
Less(i, j int) bool
// Swap方法交换索引i和j的两个元素
Swap(i, j int)
}
*/
func (stu StuSlice) Len() int {
return len(stu)
}
func (stu StuSlice)Less(i, j int) bool {
return stu[i].Score < stu[j].Score
}
func (stu StuSlice) Swap(i, j int) {
stu[i], stu[j] = stu[j], stu[i]
}
func main() {
//创建一个Student切片实例
var stues StuSlice
for i := 0; i <=10 ; i++ {
stu := Student{
Name : fmt.Sprintf("学生%v", rand.Intn(100)),
Age : rand.Intn(100),
Score : float64(rand.Intn(100)),
}
//把stu append 到stues结构体实例变量中
stues = append(stues, stu)
}
//排序前数据
for _,v := range stues {
fmt.Println(v)
}
//排序后数据
fmt.Println("排序后数据")
sort.Sort(stues)
for _,v := range stues {
fmt.Println(v)
}
}
6.接口和继承的比较
package main
import(
"fmt"
)
//声明一个鸟儿接口
type BridAble interface {
Flying()
}
//声明一个鱼儿接口
type FishAble interface {
Swimming()
}
//Monkey
type Monkey struct{
Name string
}
func (this *Monkey) climbing() {
fmt.Println(this.Name, "会爬树")
}
//LittleMonkey结构体
type LittleMonkey struct{
Monkey // 继承
}
//让LittleMonkey实现BridAble.Flying
func (this *LittleMonkey) Flying() {
fmt.Println(this.Name, "通过学习,学会了飞翔")
}
//让LittleMonkey实现FishAble.Swimming
func (this *LittleMonkey) Swimming() {
fmt.Println(this.Name, "通过学习,学会了游泳")
}
func main() {
//创建一个LitteMonkey实例
littleMonkey := LittleMonkey{
Monkey {
Name : "猴子1",
},
}
littleMonkey.climbing()
littleMonkey.Flying()
littleMonkey.Swimming()
}
对上面代码的小结 :
(1).当 A 结构体继承了 B 结构体,那么 A 结构就自动的继承了 B 结构体的字段和方法,并且可以直接使用
(2 ).当 A 结构体需要扩展功能同时不希望去破坏继承关系,则可以去实现某个接口即可,因此可以认为:实现接口是对继承机制的补充
图示说明:
接口和继承解决的解决的问题不同
继承的价值主要在于:解决代码的复用性和可维护性
接口的价值主要在于:设计,设计好各种规范(方法),让其它自定义类型去实现这些方法
接口比继承更加灵活 Person Student , BirdAble LitdeMonkev
接口比继承更加灵活,继承是满足is-a的关系,而接口只需满足 like - a的关系
接口在一定程度上实现代码解藕