第五篇:接口
第五篇:接口
一、接口的基本使用
1 接口的介绍
接口:面向对象的领域中,接口一般这样定义,接口定义一个对象的行为,来规范子类对象的行为。
接口是一系列方法的结合【规范行为】。
go和python一样,也是鸭子类型;python中使用abc模块
进行约束。go和python虽然推崇鸭子类型,但是在开发过程中,接口还是非常重要的。【有鸭子里面的属性,就叫鸭子,如现在有个鸭子类,内有speak方法 有run方法, 子类只要实现了speak和run,我就认为子类是鸭子】;Java中有接口,子类必须实现鸭子类的所有方法,子类才叫鸭子。
2 定义接口
2.1 接口的简单实用
// 侵入式接口(java 接口没了,子类报错)和非侵入是接口(接口没了,不影响代码,go和python语言中的接口是非侵入式的)
// 1 接口的简单使用
// 定义一个鸭子接口
type Duck interface {
speak() // speak方法,这里虽然是函数,但是叫方法
run()
//run(name string) (int, int) // run方法
}
//定义一个普通鸭子结构体
type PDuck struct {
name string
sex string
age int
}
//定义一个唐老鸭结构体
type TDuck struct {
name string
sex string
age int
wife string
}
// 让普通鸭子和唐老鸭都实现duck接口
// 结构体实现一个接口: 只要绑定接口中的所有方法,就叫实现该接口【只要写完,编辑器就提示已经绑定,太牛了】
func (p PDuck)speak() {
fmt.Println("普通鸭子嘎嘎叫,普通鸭子名字叫",p.name)
}
func (p PDuck)run() {
fmt.Println("普通鸭子歪歪扭扭走了,普通鸭子名字叫",p.name)
}
//唐老鸭也实现Duck接口
func (p TDuck)speak() {
fmt.Println("唐老鸭说人话,唐老鸭子名字叫",p.name)
}
func (p TDuck)run() {
fmt.Println("唐老鸭人走路,唐老鸭子名字叫",p.name)
}
2.2 多态的概念【接口实现】
接口的实际使用,接口也是一个类型。其实和Java中的多态一样。【其实这就叫做多态,同一类事物的多种形态】**
【实现接口,只要实现了接口中的所有方法,就会自动进行接口绑定】
// 定义一个鸭子接口
type Duck interface {
speak() // speak方法,这里虽然是函数,但是叫方法
run()
//run(name string) (int, int) // run方法
}
//定义一个普通鸭子结构体
type PDuck struct {
name string
sex string
age int
}
//定义一个唐老鸭结构体
type TDuck struct {
name string
sex string
age int
wife string
}
// 让普通鸭子和唐老鸭都实现duck接口
// 结构体实现一个接口: 只要绑定接口中的所有方法,就叫实现该接口【只要写完,编辑器就提示已经绑定,太牛了】
func (p PDuck)speak() {
fmt.Println("普通鸭子嘎嘎叫,普通鸭子名字叫",p.name)
}
func (p PDuck)run() {
fmt.Println("普通鸭子歪歪扭扭走了,普通鸭子名字叫",p.name)
}
//唐老鸭也实现Duck接口
func (p TDuck)speak() {
fmt.Println("唐老鸭说人话,唐老鸭子名字叫",p.name)
}
func (p TDuck)run() {
fmt.Println("唐老鸭人走路,唐老鸭子名字叫",p.name)
}
func main() {
//// 1 得到一个普通鸭子对象
//pduck := PDuck{"小黑", "母", 2}
////2 得到一个堂老鸭子对象
//tduck:=TDuck{"小唐","男",1,"刘亦菲"}
var duck Duck // 生成一个接口对象
duck = PDuck{"小黑", "母", 2} // 多态使用
fmt.Println(duck) // {小黑 母 2} 此时duck是接口,和平时打印pduck是一样的
// 接口没有属性,只能.到接口中的两个方法
duck.speak() // 普通鸭子嘎嘎叫,普通鸭子名字叫 小黑【执行看左边,接口中存在的方法】
}
2.3 接口内部表示
我们可以把接口看作内部的一个元组 (type, value)。type 是接口底层的具体类型(Concrete Type),而 value 是具体类型的值。
2.4 接口类型转成struct
将接口转化成struct之后,就可以拿到原来的属性和自由方法,不然的话,接口类型只能调用,接口中存在的方法。【Java中进行强制类型转换即可,python中根本没有这个概念,或者说,python压根用不到多态
,因为参数中可以传递任何类型的东西】
var duck Duck
duck = PDuck{"小黑", "母", 2}
fmt.Println(duck) // {小黑 母 2} 此时duck是接口,和平时打印pduck是一样的
// 接口没有属性,只能.到接口中的两个方法
duck.speak() // 普通鸭子嘎嘎叫,普通鸭子名字叫 小黑【执行看左边,接口中存在的方法】
类型断言:具体模板v, ok := duck.(PDuck)
,如果断言成功,v
为具体类型,ok
为true。
// 断言成功
v, ok := duck.(PDuck) // 将类型进行转换
if ok {
fmt.Println(v.name) // 可以对原来的属性进行访问
}
// 断言失败
// v为被断言类型的零值;ok为false
类型选择(通过switch类实现)
// 补充一个奇怪的写法 if后先进行声明,之后ok为true的话。
if v, ok := duck.(PDuck); ok {
fmt.Println("我是普通鸭子")
fmt.Println(v.name)
}else if v, ok := duck.(TDuck); ok{
fmt.Println("我是唐老鸭")
fmt.Println(v.name)
}
// 使用switch进行替换,即可
func test(duck Duck) {
switch duck.(type) { // type是一个关键字
case PDuck:
fmt.Println("我是普通鸭子")
case TDuck:
fmt.Println("我是唐老鸭")
default:
fmt.Println("我是鸭子这个类")
}
}
func main() {
var duck Duck
duck = PDuck{"小黑", "母", 2}
test(duck) // 我是普通鸭子
duck = TDuck{"小唐","男",1,"刘亦菲"}
test(duck) // 我是唐老鸭
}
// 补充: 断言成功,如何拿到断言的对象
func test(duck Duck) {
switch v := duck.(type) { // v := duck.(type)
case PDuck:
fmt.Println(v) // 此时v为断言成功的对象
fmt.Println("我是普通鸭子")
case TDuck:
fmt.Println("我是唐老鸭")
default:
fmt.Println("我是鸭子这个类")
}
}
// 使用switch进行替换,即可
func test5(b Empty) {
switch v:=b.(type) {
case string:
fmt.Println("我是字符串")
fmt.Println(v)
case int:
fmt.Println("我是int")
fmt.Println(v)
case [3]int:
fmt.Println("我是数组")
fmt.Println(v)
}
}
2.5 空接口
空接口,没有任何方法,所有数据类型都实现了空接口。
// 空接口定义【】
type Empty interface {
}
// 空接口使用
var a int = 10
var b string = "yangyi"
var c [3]int
var e Empty
e = a
e = b
e = c
// 空接口的话,我们也可以实现函数传递任何参数,但是使用的话,必须进行断言回来
func test(b Empty) {
switch v:=b.(type) {
case string:
fmt.Println("我是字符串")
fmt.Println(v)
case int:
fmt.Println("我是int")
fmt.Println(v)
case [3]int:
fmt.Println("我是数组")
fmt.Println(v)
}
}
test(e)
2.6 匿名空接口
一般用在形参上。
func test(b interface()){
}
// 泛型: 空接口的话,需要进行断言回来; 泛型的话,传什么就是什么,比如说反转函数,反转数字算一个,反转字符串算一个,那还不如写一个统一的泛型。【java和c++中就是如此】
func append(slice []Type, elems ...Type) []Type
接口是运行时多态,泛型是编译期多态。尽量用泛型。区别就在于机制作用时机不同,不是所有情况都可以用泛型在编译的时候确定。
// 如此一来,之前学过的所有的集合类型,都可以放到接口类型
// 1 数组
var a [3]Duck
a[1]=PDuck{}
a[2]=TDuck{}
// 2 切片
var a []Duck = make([]Duck, 3)
// 3 map
var a map[string]interface{} = make(map[string]interface{})
a["name"] = "yangyi"
a["age"] = 18
// 总结: 虽然放着很开心,但是用着就需要断言,就不行了,哈哈。。。
二、接口的嵌套与实现多个接口
1 实现多个接口
书写完多个接口中的所有方法,便会自动实现几个接口。一旦转到某个接口,只能使用接口的方法,自身属性和自身方法需要类型断言后才能使用。
// 1 实现多个接口
type Duck interface {
speak() //speak()方法
run()
}
type Animal interface {
eat()
sleep()
}
//定义一个唐老鸭结构体
type TDuck struct {
name string
sex string
age int
wife string
}
//唐老鸭实现Duck接口
func (p TDuck)speak() {
fmt.Println("唐老鸭说人话,唐老鸭子名字叫",p.name)
}
func (p TDuck)run() {
fmt.Println("唐老鸭人走路,唐老鸭子名字叫",p.name)
}
//唐老鸭实现Animal接口
func (p TDuck)sleep() {
fmt.Println("唐老鸭说人话,唐老鸭子名字叫",p.name)
}
func (p TDuck)eat() {
fmt.Println("唐老鸭人走路,唐老鸭子名字叫",p.name)
}
2 接口嵌套
// 目的: 就是为了少些一点接口【类似于面向对象的父类】
type Animal interface {
eat()
sleep()
}
type Duck interface {
Animal
speak()
run()
}
type Cat interface {
Animal
speak()
run()
}
// 定义普通鸭子结构体
type PDuck struct {
name string
sex string
age int
}
// 实现duck接口
func (p PDuck)speak() {
}
func (p PDuck)run() {
}
func (p PDuck)sleep() {
}
func (p PDuck)eat() {
}
3 接口的零值
var a Animal //nil 是引用类型 a就存2值,一个是类型,一个是指向该类型的指针
fmt.Println(a)
在Go语言中,布尔类型的零值(初始值)为 false,数值类型的零值为 0,字符串类型的零值为空字符串
""
,而指针、切片、映射、通道、函数和接口的零值则是 nil。