第五篇:接口

第五篇:接口

一、接口的基本使用

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。

posted @ 2023-04-03 20:44  YangYi215  阅读(43)  评论(0编辑  收藏  举报