go语言接口基础 原文参考:Go语言基础之接口 | 李文周的博客
package main
import (
"fmt"
)
//接口
//接口定义了一个对象的行为规范,之定义规范不是先,有具体的对象来事项规范的细节
//一个对象只要全部是先了接口中定义的方法,那么就实现了这个接口,换句话说,接口就是一个需要实现方法的列表.
//定义一个Sayer接口
// Sayer 接口
type Sayer interface {
say()
}
//定义dog,cat结构体
type dog struct{}
type cat struct{}
//因为Sayer中只有一个Say方法,所以我们只要是给dog和cat是先say方法就代表是先了sayer接口
func (d dog) say() {
fmt.Println("汪汪汪..")
}
func (c cat) say() {
fmt.Println("喵喵喵...")
}
//接口变量,以及使用
func main() {
var x Sayer
a := cat{} //将结构体赋值给一个变量
b := dog{}
x = a //将绑定结构体数据的变量赋予Sayer接口
x.say() //赋值后的接口直接调用已经有的say()方法.这里say方法是与cat结构体绑定的.
var y Sayer //为了更加便于理解,这里又声明了一个y变量是sayer接口.
y = b //同理将结构体数据的对应的变量b赋予x,sayer方法.
y.say() //已经包含结构体数据的接口直接调用say方法.
}
输出结果
C:\Users\34826\AppData\Local\Temp\___go_build_main_go__1_.exe #gosetup
喵喵喵...
汪汪汪..
//总结下,就是定义接口,内部有方法名称以及返回值, //定义一个需要绑定该方法的结构体,可以定义结构体内部的字段信息 //定义一个方法,这个方法要将结构体和接口进行绑定.这个过程用于后续直接使用结构体来调用对应创建的方法. //总结就是接口定义方法名称-->结构体定义字段数据内容-->绑定函数传入数据,并定义方法的行为动作.最后在需要的地方进行调用.
go语言值接收者和指针接收者的区别
//值接收者和指针接收者实现接口的区别
type Mover interface {
move()
}
type dog struct {
}
//值接收者实现接口
func (d dog) move() {
fmt.Println("狗会动..")
}
//实现接口的dog类型
func main() {
var x Mover
d := dog{}
x = d
x.move()
var y Mover
fugui := &dog{}
y = fugui
y.move()
}
输出结果
C:\Users\34826\AppData\Local\Temp\___go_build_main_go__1_.exe #gosetup
狗会动..
狗会动..
这里使用的是值接收者,这里的值的意思是结构体与方法绑定的时候结构体接受方法使用的是哪类接收者.
指针接收者在传递结构体的时候只能使用指针类型.而值接收者可以随意使用指针或者值进行传递.
type Mover interface {
move()
}
type dog struct {
}
//值接收者实现接口
func (d *dog) move() {
fmt.Println("狗会动..")
}
//指针接受在输出结果
func main() {
var x Mover
d := dog{}
x = &d //这里dog类型使用的是值传递.但是在绑定move方法的时候是用的*dog,所以x =d是无法直接传值给x的.这个位置应该用&d,表示指针传递.
x.move()
var y Mover
a := &dog{} //这里dog{}使用的就是指针赋值,那么在下面的 y = a 的时候move接收者的方法是指针接收者.所以可以直接使用.
y = a
y.move()
}
输出结果
C:\Users\34826\AppData\Local\Temp\___go_build_main_go__1_.exe #gosetup
狗会动..
狗会动..
判断代码是否完成编译(面试题)
type Peopler interface {
Speak(string) string
}
type Student struct {
}
func (stu *Student) Speak(think string) (talk string) {
if think == "sb" {
talk = "你是个大帅比"
} else {
talk = "你好"
}
return
}
func main() {
//var peo Peopler = Student{} //这里是无法编译过的因为 people是一个接口类型,Student是一个结构体类型.所以无法直接进行定义.
var peo Peopler //定义peo为people接口类型
s := Student{}
think := "bitch"
peo = &s //这里是指针类型的接收者所以要用&s
fmt.Println(peo.Speak(think))
}
输出结果
C:\Users\34826\AppData\Local\Temp\___go_build_main_go__1_.exe #gosetup
你好
注意:
如果不修改的话是无法编译成功的,具体原因参见代码注释部分.
多个接口使用一个接收者
//一个类型实现多个接口
type Sayer interface {
say()
}
type Mover interface {
move()
}
type dog struct {
name string
}
//实现sayer接口
func (d dog) say() {
fmt.Printf("%s会汪汪汪...\n", d.name)
}
func (d dog) move() {
fmt.Printf("%s会动\n", d.name)
}
func main() {
var x Sayer
var y Mover
a := dog{name: "大兄弟"}
x = a
y = a
x.say()
y.move()
}
输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go__1_.exe #gosetup
大兄弟会汪汪汪...
大兄弟会动
//这里在书写的时候要仔细看下是否将绑定的方法写成了接口名字.如果是的话那代码同样是编译不过去的.
多个类型实现同一个接口(多个结构体,类型)
//多个类型实现同一个接口
type Mover interface {
move()
}
type dog struct {
name string
}
type car struct {
brand string
}
func (d dog) move() {
fmt.Printf("%s会跑...\n", d.name)
}
func (c car) move() {
fmt.Printf("%s跑的贼快...\n", c.brand)
}
func main() {
var x Mover
a := dog{name: "大兄弟.."}
x = a
x.move()
var y Mover
b := car{brand: "比亚迪..."}
y = b
y.move()
}
输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go__1_.exe #gosetup
大兄弟..会跑...
比亚迪...跑的贼快...
说明一下:
这里只定义了一个mover接口,但是 dog类型,car类型都适用这个接口.那么只要定义一个接口就可以根据不同的绑定类型输出不同的结果.具体省不省代码还得看项目.
多个类型实现分别实现接口中的某个方法.从而完整实现接口
type WashingMachiner interface {
wash()
dry()
}
type dryer struct {
name string
}
func (d dryer) dry() {
fmt.Printf("%s甩干\n", d.name)
}
type haier struct {
dryer //这里很关键.类型中嵌入其他类型,那么haier也实现了dryer实现的方法.那么
}
func (h haier) wash() {
fmt.Println("洗洗洗...")
}
func main() {
var x WashingMachiner
a := haier{dryer{name: "来啦老弟"}} //这里使用的是类型嵌套的方式来进行赋值操作的.
x = a
x.dry()
x.wash()
}
输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go__1_.exe #gosetup
来啦老弟甩干
洗洗洗...
说明一下:
这里使用的结构体嵌套的方式来实现了拥有两个方法的接口,在进行接口数据赋值的时候与嵌套结构体的赋值方式是一样的.但是根本上dryer结构体是使用haier结构体实现的接口.过程如下 :
定义含有多个方法的接口wash interface==> 定义dryer结构体为类型1
==> 定义dryer类型实现接口的其中一个方法 dry() ,这里是通过dry()方法绑定了一个dryer结构体.
==> 定义一个haier结构体为类型2
==>将dryer结构体定义到haier结构体中,就是haier包含dryer结构体.
==>使用wash()方法绑定haier结构体, 到此为止接口和类型,方法之间全部定义完成 但是这里只有haier类型实现了接口,dryer类型是haier类型的一部分构成.如果二者缺一个都无法实现wash接口.
==>通过haier对象调用wash接口的dry和wash方法,其中dry方法调用过程中在赋值的时候要找到haier.dryer.name进行赋值,否则是无效的.
==>因为接口的两个方法haier结构体都实现了.所以可以直接进行方法的调用.
接口的嵌套
//接口的嵌套
type Sayer interface {
say()
}
type Mover interface {
move()
}
type Animal interface { //Animal类型接口实现了sayer,mover接口,可以直接看出Animal实现了say()方法和move()方法.
//那么下面的方法绑定就可以直接理解成绑定了Animal接口.如果animal接口不存在.那么就表示cat类型分别绑定了sayer 和 mover接口.
//相当于一个类型绑定多个接口的形式.参考上面的内容查看.
Sayer
Mover
}
type cat struct {
name string
}
func (c cat) say() {
fmt.Printf("%s会叫...\n", c.name)
}
func (c cat) move() {
fmt.Printf("%s会跑...\n", c.name)
}
func main() {
var x Animal //这里直接定义成x 是Animal类型,
c := cat{name: "大兄弟"}
x = c
x.move()
x.say()
}
输出结果如下:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go__1_.exe #gosetup
大兄弟会跑...
大兄弟会叫...
空接口
//空接口
//空接口是指没有指定任何方法的接口.因此任何类型都实现了空接口.
//空接口类型的变量可以存储任意类型的变量
func main() {
var x interface{}
s := "hello 大侠"
x = s
fmt.Printf("%T类型,%s\n", x, x) //string类型,hello 大侠 这里s 是一个字符串类型,将s 赋值给x后(不需要再次进行定义空接口满足任何类型),那么x也变成了string类型的存储.
i := 100
x = i
fmt.Printf("type:%T value:%v\n", x, x) //type:int value:100
b := true
x = b
fmt.Printf("type:%T value:%v\n", x, x) //type:bool value:true
}
输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go__1_.exe #gosetup
string类型,hello 大侠
type:int value:100
type:bool value:true
空接口存储啥类型的值自身就变成啥类型.大概就是一个值的类型将接口类型覆盖这么个特性.
空接口的应用
//空接口应用
//空接口作为函数的参数
func show(a interface{}) {
fmt.Printf("type:%T,value:%v", a, a)
}
func main() {
show("asd")
}
输出结果
C:\Users\34826\AppData\Local\Temp\___go_build_main_go__1_.exe #gosetup
type:string,value:asd
//空接口做为map的值
func main() {
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "沙河小王子"
studentInfo["age"] = 12
studentInfo["married"] = false
fmt.Println(studentInfo)
}
输出结果
C:\Users\34826\AppData\Local\Temp\___go_build_main_go__1_.exe #gosetup
map[age:12 married:false name:沙河小王子]
类型断言.只适用于接口
//类型断言
func main() {
var x interface{}
x = "沙河"
value, ok := x.(string)
if ok {
fmt.Println("string 类型")
fmt.Println(value)
} else {
fmt.Println("非string类型,类型断言失败.")
}
}
输出结果
C:\Users\34826\AppData\Local\Temp\___go_build_main_go__1_.exe #gosetup
string 类型
沙河
类型断言只试用于接口.因为接口可以存储任何类型的值,那么如果要利用起来这些值的话就要先判断这些值得类型.咋判断呢?猜呗.那就是类型断言.
多类型断言用switch进行多路判断.
//类型断言
func main() {
var x interface{}
x = "沙河"
switch v := x.(type) {
case string:
fmt.Printf("x is string , value is %v\n", v)
case int:
fmt.Printf("x is int , value is %v\n", v)
case bool:
fmt.Printf("x is bool , value is %v\n", v)
default:
fmt.Println("un support type! ")
}
}
输出结果:
C:\Users\34826\AppData\Local\Temp\___go_build_main_go__1_.exe #gosetup
x is string , value is 沙河
说明一下:
x.type意思就是x的类型是什么.会有一个类型返回值.那么就需要根据这个类型返回值进行判断应该做什么样的操作.switch就是罗列出来多种情况以供选择.(当然也有断言不到的情况,因为接口类型是活得,而任何一个变量都可以声明成一种类型,所以声明的类型可以是无数个,)但是基础类型大概就那么几个..