接口
接口是一种类型,可以做参数,变量,返回值设置类型。它不用关心具体的类型,只关心调用什么方法。也就是定义了一个行为规范,只定义规范不实现,由具体的对象来实现规范的细节。
只要一个变量实现了接口中规定的所有的方法,那么就是这个接口的变量。
接口是一种动态引用类型,包括了值的类型和值的类型,会随着给他赋值的类型的值和类型进 行变换
package main
import "fmt"
// 接口格式
// type 接口类型名 interface{
// 方法名1( 参数列表1 ) 返回值列表1
// 方法名2( 参数列表2 ) 返回值列表2
// …
// }
// 接口类型名常用er结尾
//将具有同样的方法的类型定义为一种类型,这样调用共同的方法时可以直接用接口的类型
type speaker interface {
speak() //只要实现了speak方法的变量都是Speaker类型
}
type cat struct{}
type dog struct{}
type person struct{}
func (c cat) speak() {
fmt.Println("喵喵喵~")
}
func (d dog) speak() {
fmt.Println("汪汪汪~")
}
func (p person) speak() {
fmt.Println("啊啊啊~")
}
//只要实现了speak方法都都是speaker类型,这些类型都能作为函数的参数
func attack(x speaker) {
x.speak()
}
func main() {
var c1 cat
var d1 dog
var p1 person
var x speaker
attack(c1)
attack(d1)
attack(p1)
//可以直接将属于speaker类型的实例赋值给speaker类型的变量,然后执行对应的方法
//接口类型的变量存储变量的类型和变量的值,所以根据给它赋值的变量会自动去改变类型和值
x = c1 //变为cat类型
fmt.Printf("%T\n", x)
attack(x)
x = d1 //变为dog类型
fmt.Printf("%T\n", x)
attack(x)
}
package main
import "fmt"
//接口里的方法的参数和返回值可以只写类型,不写名称
//要实现所有的方法才能是这个接口类型,注意名字相同但参数不同的不算同一个函数
type animal interface {
move()
eat(string)
}
type cat struct {
name string
feet int8
}
type chicken struct {
feet int8
}
func (c chicken) move() {
fmt.Println("鸡在跑")
}
func (c chicken) eat() {
fmt.Println("鸡吃饲料")
}
func (c cat) move() {
fmt.Println("猫在跑")
}
func (c cat) eat(food string) {
fmt.Printf("猫吃%s\n", food)
}
func main() {
var a1 animal
c1 := chicken{
feet: 2,
}
c2 := cat{
feet: 4,
name: "喵喵",
}
fmt.Println(c1, c2)
c1.move()
c1.eat()
// a1 = c1出错因为chicken实现的eat方法不是interface里要求的带参数的类型,所以不算
a1 = c2
a1.eat("小黄鱼")
fmt.Println(a1)
}
-
值接收者和指针接收者实现接口
使用值接受者实现所有的接口的方法,那么接口变量能够存储该结构体本身和结构体的指针
使用指针接受者实现所有的接口方法,那么接口变量只能够存储该结构体的指针package main import "fmt" type animal interface { move() eat(string) } type cat struct { name string feet int8 } //使用值接受者实现所有接口的方法 func (c cat) move() { fmt.Println("猫在跑") } func (c cat) eat(food string) { fmt.Printf("猫吃%s\n", food) } type chicken struct { feet int8 } //使用指针接受者实现所有接口的方法 func (c *chicken) move() { fmt.Println("鸡在跑") } func (c *chicken) eat(food string) { fmt.Printf("鸡吃%s\n", food) } func main() { //使用值接受者实现所有的接口方法,那么接口变量可以存该类型的结构体类型和结构体指针类型 c1 := cat{ name: "tom", feet: 4} c2 := &cat{ name: "假老练", feet: 4} var a animal a = c1 fmt.Println(a) a = c2 fmt.Println(a) //使用指针接受者实现所有的接口方法,那么接口变量只能存该结构体的指针类型 // c3 := chicken{ // feet: 4} c4 := &chicken{ feet: 8} // a = c3出错,因为使用的指针接受者实现接口方法后,接口类型智能接受结构体指针类型 fmt.Println(a) a = c4 fmt.Println(a) }
-
接口与类型的关系
一个类型可以实现多个接口,多个类型可以实现一个接口
package main import "fmt" type Sayer interface { say() } type Mover interface { move() } type dog struct { name string } func (d dog) say() { fmt.Printf("%s会叫汪汪汪\n", d.name) } func (d dog) move() { fmt.Printf("%s会动\n", d.name) } type car struct { brand string } func (c car) move() { fmt.Printf("%s在公路上飞奔\n", c.brand) } func main() { a := dog{name: "旺财"} //一个类型可以实现多个接口 a.say() a.move() //多个类型可以实现同一个接口 b := car{brand: "保时捷"} b.move() }
-
接口的嵌套
接口的方法可以通过在类型中嵌入其他类型或者结构体来实现,即嵌入一个实现了一个方法的结构体,则被嵌入的结构体也有了这个方法
接口和接口之间直接嵌套可以创造出新的接口
package main import "fmt" type WashingMachine interface { wash() dry() } type dryer struct{} func (d dryer) dry() { fmt.Println("甩一甩") } //接口的方法可以通过在类型中嵌入其他类型或者结构体来实现 //将结构体嵌入,则被嵌入的结构体也有这个方法 type haier struct { dryer } //haier实现另外一个方法,则满足了interface的条件 func (h haier) wash() { fmt.Println("洗刷刷") } // Sayer 接口 type Sayer interface { say() } // Mover 接口 type Mover interface { move() } // 接口嵌套 type animal interface { Sayer Mover } type cat struct { name string } //cat实现了两个方法,则也满足animal的接口要求 func (c cat) say() { fmt.Println("喵喵喵") } func (c cat) move() { fmt.Println("猫会动") } func main() { h1 := haier{} var x WashingMachine = h1 x.dry() x.wash() //cat实现了嵌套里的两个接口的方法,所以也属于animal接口类型 var y animal = cat{name: "花花"} y.move() y.say() }
-
空接口
空接口是没有定义任何方法的接口,所以任何类型都实现了空接口。空接口类型的变量可以存储任意类型的变量。
package main import "fmt" //空接口用interface{}表示,是一种类型,表示任意类型 //interface关键字,interface{}空接口类型 // 空接口作为函数参数,可以接受所有类型参数 func show(a interface{}) { fmt.Printf("type:%T value:%v\n", a, a) } func main() { //空接口作为类型,可以接收所有类型 var m1 map[string]interface{} = make(map[string]interface{}, 16) m1["name"] = "周林" m1["age"] = 9000 m1["married"] = true m1["hobby"] = [...]string{"唱", "跳", "rap"} fmt.Println(m1) show(false) show(nil) show(m1) }
-
类型断言
只有接口类型能做类型断言。
package main import ( "fmt" ) //类型断言x.(T),x为interface{}类型,T表示可能的类型,返回两个参数,第一个是转换为T类型的值,一个是判断是否正确的bool值 func assignstring(a interface{}) { str, ok := a.(string) if !ok { fmt.Println("wrong") } else { fmt.Printf("传进来的是字符串%s\n", str) } } //使用switch可以断言多次进行判断 //如果断言只用一个值接受则就是用断言类型转换后的值 func assign2(a interface{}) { switch t := a.(type) { case string: fmt.Printf("x is a string,value is %v\n", t) case int: fmt.Printf("x is a int is %v\n", t) case bool: fmt.Printf("x is a bool is %v\n", t) default: fmt.Println("unsupport type!") } } func main() { assignstring(100) assign2("gogogogo") assign2(true) assign2(555) }