接口是啥
在面向对象的领域里,接口一般这样定义:接口定义一个对象的行为。接口只指定了对象应该做什么,至于如何实现这个行为(即实现细节),则由对象本身去确定。
在 Go 语言中,接口就是方法签名(Method Signature)的集合。当一个类型定义了接口中的所有方法,我们称它实现了该接口。这与面向对象编程(OOP)的说法很类似。接口指定了一个类型应该具有的方法,并由该类型决定如何实现这些方法。
接口:面向对象的领域里,接口一般这样定义;接口定义一个对象的行为
鸭子类型
简单来说
鸭子类型 有个鸭子。speak方法,有run方法,子类只要实现了两个方法,我就认为是鸭子,从下往上的(go也是鸭子类型)
接口的定义和使用
接口是一系列方法的集合(规范行为),结构体是一系列属性的集合
package main
import "fmt"
// go也是鸭子类型
// 接口:面向对象的领域里,接口一般这样定义;接口定义一个对象的行为
// 实际开发中接口还是很重要的,
// 鸭子类型 有个鸭子。speak方法,有run方法,子类只要实现了两个方法,我就认为是鸭子,从下往上的
// 接口是一系列方法的集合(规范行为),结构体是一系列属性的集合
// 定义接口(定义一个鸭子接口,speak,run方法)
type Duck interface {
// 方法名 参数 返回值
speak(word string) //speak方法
run() string //run方法
}
// 定义普通鸭子结构体
type PDuck struct {
Id int
Name string
}
// 定义一个唐老鸭结构体
type TDuck struct {
Id int
Name string
Color string
}
// 目前为止他们没有任何关系,但是想让两个鸭子都实现duck接口
// 结构体实现一个接口,只要绑定接口中的所有方法,就叫实现该接口
func (p PDuck) speak(word string) {
fmt.Println("普通鸭子正在叫!",word)
}
func (p PDuck) run() string {
return "普通鸭子 正在跑!"
}
func (t TDuck) speak(word string){
fmt.Println("唐老鸭正在叫!")
}
func (t TDuck) run() string {
return "唐老鸭 正在跑!"
}
func main() {
// 1得到一个普通鸭子对象
pduck := PDuck{1,"肉鸭"}
// 得到一个唐老鸭对象
tduck := TDuck{1,"唐老鸭","white"}
pduck.speak("嘎嘎嘎")
res := tduck.run()
fmt.Println(res)
// 侵入式接口()接口没了,子类报错
// 非侵入式接口(接口没了,不影响代码) 装饰器就是非侵入式接口
}
--------------------------------------------------
普通鸭子正在叫! 嘎嘎嘎
唐老鸭 正在跑!
Go属于非侵入式接口
接口的内部使用
把接口类型转成struct,属性,自有方法也有了
类型断言
package main
import "fmt"
// 接口内部表示
// 我们可以把接口看做内部的一个元祖(type value)
// type 是接口底层的具体类型(concrete type),而value 是具体类型的值
type Dog interface {
run()
eat(food string)
}
type Wolf struct {
name string
kind string
}
type GaoF struct {
name string
kind string
color string
}
func (Gf GaoF) run() {
fmt.Println("普通的狗正在跑!")
}
func (Gf GaoF) eat(food string) {
fmt.Println("普通的狗正在吃:", food)
}
func (wolf Wolf) run() {
fmt.Println("狼正在奔跑!")
}
func (wolf Wolf) eat(food string) {
fmt.Println("狼正在吃:", food)
}
// 4 把接口类型转成struct,属性,自有方法也有了
func main() {
// 类型断言
var wolf Dog= Wolf{"哈士奇", "大傻子"}
// 断言是Wolf类型,如果成功,ok是True,v就是结构体对象
v1, ok1:= wolf.(Wolf)
fmt.Println(v1)
fmt.Println(ok1)
fmt.Println(v1.name)
// 断言是GaoF类型,断言失败ok是false v是类型的空值,因为没有赋值
v2,ok2 := wolf.(GaoF)
fmt.Println(v2)
fmt.Println(ok2)
}
但是这种并不是常用的方式
常用方式(类型选择器)
通过switch来实现
package main
import "fmt"
// 接口内部表示
// 我们可以把接口看做内部的一个元祖(type value)
// type 是接口底层的具体类型(concrete type),而value 是具体类型的值
type Dog interface {
run()
eat(food string)
}
type Wolf struct {
name string
kind string
}
type GaoF struct {
name string
kind string
color string
}
func (Gf GaoF) run() {
fmt.Println("普通的狗正在跑!")
}
func (Gf GaoF) eat(food string) {
fmt.Println("普通的狗正在吃:", food)
}
func (wolf Wolf) run() {
fmt.Println("狼正在奔跑!")
}
func (wolf Wolf) eat(food string) {
fmt.Println("狼正在吃:", food)
}
func test(dog Dog) {
if v, ok := dog.(GaoF); ok {
fmt.Println("我是高夫!")
fmt.Println(v)
} else if v, ok := dog.(Wolf); ok {
fmt.Println("我是狼!")
fmt.Println(v)
}
}
// 试用switch 选择成功,拿到结构体对象
func test2(dog Dog) {
switch v:=dog.(type) {
case GaoF:
// v在哪个下面这个V就是谁
fmt.Println(v.color)
fmt.Println("我是高夫!")
case Wolf:
fmt.Println(v.name)
fmt.Println("我是狼!")
default:
fmt.Println("我是鸭子类型!")
}
}
// 4 把接口类型转成struct,属性,自有方法也有了
func main() {
// 类型断言
var wolf Dog = Wolf{"哈士奇", "大傻子"}
var gf Dog = GaoF{"高夫", "迪士尼IP", "black"}
test(gf)
test(wolf)
test2(gf)
test2(wolf)
}
也就是说我现在可以把一个结构体类型转换成接口类型
空接口
任何方法都没有,所有数据类型都实现了空接口
package main
import "fmt"
// 定义空接口
type Cat interface {
}
func test3(c Cat) {
switch v:=c.(type) {
case int:
fmt.Println(v)
fmt.Println("我是int类型!")
case string:
fmt.Println(v)
fmt.Println("我是string类型!")
case Cat:
fmt.Println("空类型")
}
}
func main() {
var a int = 1
var b string = "猫猫"
var c [3]int
var d Cat //空接口类型
d = a
d = b
d = c
fmt.Println(d)
test3(a)
}
// 小提示泛型
// 所谓的泛型就是等你来了之后再去确定你是什么类型
匿名空接口
package main
import "fmt"
func test4(b interface{}) {
switch v := b.(type) {
case int:
fmt.Println(v)
fmt.Println("int类型!")
case [4]int: // 不能使用[...]int 判断
fmt.Println(v)
fmt.Println("数组类型")
}
}
func main() {
a :=[...]int{1, 23, 4, 5}
test4(a)
}
接口的嵌套
package main
import "fmt"
type Cow interface {
Animal // 类似于父类
Drink()
}
// 如果结构体实现cow接口(实现Drink,和animal肿了eat sleep)
type Animal interface {
eat()
sleep()
}
type RedCow struct {
name string
color string
}
type WaterCow struct {
name string
color string
hobby string
}
func (rc RedCow) Drink() {
fmt.Println("红牛正在喝水!")
}
func (wc WaterCow) Drink() {
fmt.Println("水牛正在喝水!")
}
// redcow实现animal接口
func (rc RedCow) eat() {
fmt.Println("红牛正在吃!")
}
func (rc RedCow) sleep() {
fmt.Println("红牛正在睡觉!")
}
// 这样红牛即实现了cow也实现了animal接口
func main() {
var redcow = RedCow{"红牛", "红色"}
var a Animal
var b Cow
// 一旦转到某个接口上,只能使用接口的方法,自身属性和方法需要类型断言后才能使用
fmt.Println(a)
fmt.Println(b)
a = redcow
b = redcow
fmt.Println(a)
fmt.Println(b)
}
自定义集合类型
在python中集合类型不能有重复元素,可以添加修改
Make和New的区别
package main
import "fmt"
// make和new的区别
// new返回的是指向type的指针,make直接返回的是type的类型值
type Rabbit struct {
name string
sex int
hobby string
}
func main() {
//make是引用类型初始化的时候使用的
var a map[int]string = make(map[int]string)
fmt.Println(a)
var rb1 = new(Rabbit)
fmt.Println(rb1)
var rb2 = &Rabbit{}
fmt.Println(rb2)
var rb3 = make([]int, 3, 4)
fmt.Println(rb3)
var rb4 *[]int = new([]int) //他不是切片类型而是指向切片类型的指针
fmt.Println(rb4)
(*rb4) = append((*rb4), 33, 44, 55, 66)
fmt.Println(*rb4)
}
结构体取代类
一个包算是一个类,用的话导入使用
package Tiger
type Tiger struct {
Name string
Address string
Hobby string
}
func New(name string, address string, hobby string) Tiger {
return Tiger{name, address, hobby}
}