GO语言学习之面向对象(4)接口
9.接口
9.1基本介绍
按其他语言顺序,应该学多态。因为在go中,多态的特性主要通过接口来体现的
9.2为什么有接口
我们可以想想一下,生活中,接口的应用场景,手机接口,相机接口
U盘接口,这些接口不管哪个厂家生产的都遵循这个接口
开发中也一样
9.3接口快速入门
package main
import "fmt"
//定义一个接口
type Usb interface {
//声明两个没有实现的方法
Start()
Stop()
}
type Phone struct {
}
//让 Phone实现Usb接口的方法
func (p Phone)Start(){
fmt.Println("手机开始工作")
}
func (p Phone)Stop(){
fmt.Println("手机停止工作")
}
type Camera struct {
}
//让 Camera 实现 Usb接口的方法
func (c Camera)Start(){
fmt.Println("相机开始工作。。。")
}
//
func (c Camera)Stop(){
fmt.Println("相机停止工作。。。")
}
type Computer struct {
}
//编写一个方法 Working 方法,接收一个Usb接口类型变量
//只要是实现了Usb接口,(所谓实现了Usb接口,就是指实现了 Usb
//接口声明所有方法
func (c Computer)Working(usb Usb) {//usb变量会根据传入的实参,来
//通过usb借口变量来调用 start 和 stop 方法
usb.Start()
usb.Stop()
}
func main() {
//测试
//先创建结构体变量
computer:=Computer{}
phone:=Phone{}
camera:=Camera{}
//关键点
computer.Working(phone)
computer.Working(camera)
}
9.4接口概念再说明
interface类型可以定义一组方法,但是这些不需要实现,并且interface不能包含任何自定义类型(比如结构体Phone)要使用的时候,在根据具体情况把这些方法写出来(实现)
9.5基本语法
type 接口名 interface{
method1(参数列表)返回值列表
method2(参数列表)返回值列表
}
func(t 自定义类型)method1(参数列表)返回值列表{
//方法实现
}
func(t 自定义类型)method2(参数列表)返回值列表{
//方法实现
}
》小结说明:
1》接口里所有方法都没有方法体,即接口的方法都是没有实现的方法。
接口体现了程序设计的多态和高内聚低耦合的思想
2》GO中的接口,不需要显式的实现。只要一个变量,所有接口类型中的
所有变量就实现这个接口。因此,GO中没有implement这样的关键字
9.6接口使用的应用场景
对初学者讲,理解接口的概念不算太难,难的是不知道什么时候使用接口。
1》现在我们要设计轿车,专家只需把汽车需要的功能/规格定下来即可,然后让别的人具体实现即可
2》说现在有一个项目经理,管理三个程序员,开发一个软件,为了控制和管理软件,项目经理可以定义一些接口,然后由程序员实现
9.7注意事项和细节
1》接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)
package main
import "fmt"
type AInterface interface {
Say()
}
type Stu struct {
Name string
}
func (Stu Stu)Say(){
fmt.Println("stu say()")
}
func main() {
var stu Stu//结构体变量,实现了Say(),实现了 Ainterface
var a AInterface=stu
a.Say()
}
2》接口中所有的方法都没有方法体,即都是没有实现的方法
3》在go中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口
4》一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型
5》只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型
type integer int
func (i integer) Say() {
fmt.Println("integer say i=",i)
}
var i integer=10
var b AInterface=i
b.Say()
6》一个自定义类型可以实现多个接口
package main
import "fmt"
type AInterface interface {
Say()
}
type BInterface interface {
Sleep()
}
type Stu struct {
Name string
}
func (Stu Stu)Say(){
fmt.Println("stu say()")
}
func (Stu Stu)Sleep(){
fmt.Println("stu sleep()")
}
func main() {
var stu Stu//
//Stu 实现了A接口 B接口
var a AInterface=stu
var b BInterface =stu
a.Say()
b.Sleep()
}
7》go中的接口中不能有任何变量
type AInterface interface {
Age String//报错
Say()
}
8》一个接口(比如 A接口)可以继承多个别的接口(比如 B,C接口)
这时如果实现A接口,也必须将B,C接口的方法全部实现
package main
type BJIEKOU interface {
testB()
}
type CJIEKOU interface {
testC()
}
type AJIEKOU interface {
BJIEKOU
CJIEKOU
testA()
}
//如果要实现A 接口,需要将 B 接口,C 接口的方法都实现
type Stud struct {
}
func (stu Stud)testB(){
}
func (stu Stud)testC(){
}
func (stu Stud)testA(){
}
func main() {
var st Stud
var a AJIEKOU= st
a.testA()
a.testB()
a.testC()
}
9》interface类型默认是一个指针(引用类型),如果没有对interface初始化就使用,那么就会输出nil
10》空接口interface{}没有任何方法,所以所有类型都实现了空接口,即我们可以把任何一个变量赋给空接口
package main
import "fmt"
//空接口 A
type A interface{}
//结构体 x
type STU struct{
Name string
}
func main() {
var x STU
var a A=x//把变量赋给空接口
if a == nil {
fmt.Println("hello A")
}else {
fmt.Println("gogo ")
}
}
9.8作业练习
package main
import "fmt"
//接口 A
type A interface{
Test01()
Test02()
}
//接口B
type B interface{
Test01()
Test03()
}
type Stu struct {
}
func (stu Stu)Test01() {
}
func (stu Stu)Test02() {
}
func (stu Stu)Test03() {
}
func main() {
stu:=Stu{}
var a A=stu
var b B=stu
fmt.Println("hello",a,b)
}
//hello {} {}
//下面代码,你能得出什么结论
package main
import "fmt"
//接口 A
type A interface{
Test01()
Test02()
}
//接口B
type B interface{
Test01()
Test03()
}
type Stu struct {
}
func (stu Stu)Test01() {
}
func (stu Stu)Test02() {
}
func (stu Stu)Test03() {
}
type C interface {
A
B
}
//编译错误,因为C有两个test01(),编译器不能通过,报告重复定义
package main
import "fmt"
//接口 A
type A interface{
Say()
}
type Stu struct {
}
func (this *Stu)Say() {
fmt.Println("Say()")
}
func main() {
var stu Stu=Stu{}
//var a A=stu//会报stu类型没有实现A接口
var a A=&stu//这样能够实现
a.Say()
}
9.9接口编程最佳实践
实现Hero结构体切片的排序:
sort.Sort(data Interface)
package main
import (
"fmt"
"math/rand"
"sort"
)
//1.声明Hero结构体
type Hero struct {
Name string
Age int
}
//2.声明hero结构体的切片类型
type HeroSlice []Hero
//3。实现 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
}
//swap 交换元素
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,9,7,8,1}
fmt.Println(intSlice)
//可以使用系统提供的sort方法进行排序
sort.Ints(intSlice)
fmt.Println(intSlice)
//对结构体进行排序
//测试我们能否对结构体切片进行排序
var heroes HeroSlice
for i:=0;i<10;i++{
hero:=Hero{
Name: fmt.Sprintf("英雄-%d",rand.Intn(100)),
Age: rand.Intn(100),
}
//将hero 添加到 heros切片中
heroes=append(heroes,hero)
}
//排序前
for _,v:=range heroes{
fmt.Println(v)
}
//排序
sort.Sort(heroes)
//排序后
fmt.Println("排序后")
for _,v:=range heroes{
fmt.Println(v)
}
}
接口编程练习:
//1.声明Stu结构体,将 student按照分数进行排序
type Stu struct {
Name string
Age int
Score float64
}
package main
import (
"fmt"
"math/rand"
"sort"
)
//1.声明Stu结构体,将 student按照分数进行排序
type Stu struct {
Name string
Age int
Score float64
}
//2.声明 Stu 结构体切片
type StuSlice []Stu
//3.实现interface接口
//1>实现Len()方法
func (ss StuSlice)Len()int{
return len(ss)
}
//2>实现 Less()
func (ss StuSlice)Less(i,j int)bool{
return ss[i].Score>ss[j].Score
}
//3>实现 Swap()
func (ss StuSlice)Swap(i,j int){
ss[i],ss[j]=ss[j],ss[i]
}
func main() {
var stu StuSlice
for i:=0;i<5;i++{
s:=Stu{
Name: fmt.Sprintf("学生-%d",rand.Intn(100)),
Age: rand.Intn(20),
Score:float64(rand.Intn(100)),
}
//将hero 添加到 heros切片中
stu=append(stu,s)
}
//排序前
fmt.Println("排序前:")
for _,v:=range stu{
fmt.Print(v," ")
}
fmt.Println()
//排序
sort.Sort(stu)
fmt.Println("排序后:")
for _,v:=range stu{
fmt.Print(v," ")
}
fmt.Println()
}
9.10实现接口 vs 继承
package main
import "fmt"
//monkey结构体
type Monkey struct {
Name string
}
//声明接口
type BirdAble interface {
Flying()
}
//
type FishAble interface {
Swimming()
}
func (this *Monkey)climbing(){
fmt.Println(this.Name,"生来会爬树")
}
//SmallMonkey结构体
type SmallMonkey struct {
Monkey//继承
}
//让SmallMonkey实现 BirdAble
func (this *SmallMonkey)Flying(){
fmt.Println(this.Name,"通过学习,能够飞行")
}
//让 smallMonkey实现fishAble
func (this *SmallMonkey)Swimming(){
fmt.Println(this.Name,"通过学习,能够游泳")
}
func main() {
//创建一个 SmallMonkey实例
mon:=SmallMonkey{Monkey{Name:"孙大圣"}}
mon.climbing()
mon.Flying()
mon.Swimming()
}
对上面代码进行总结:
1》当 A结构体继承了B结构体,那么A结构体就自动
继承了B结构体的字段和方法,并且可以直接使用
2》当A结构体需要扩展功能,同时不希望去破坏继承关系,则可以去
实现某个接口即可,因此我们可以认为:
实现接口是对继承机制的补充
》接口和继承解决的问题不同
继承的价值主要在于:解决代码的复用性和可维护性
接口的价值主要在于:设计,设计好各种规范(方法),让其它自定义类型去实现这些方法
》接口比继承更加灵活:
接口比继承更加灵活,继承满足的是 is -a 的关系,而接口只需满足 like -a的关系
》接口在一定程度上实现了代码解耦