go-接口

接口

接口定义一个对象的行为,是一系列方法的集合

声明

接口中定义的方法是接口类型的一部分

同结构体一样,由于接口内封装的方法不同(数量,方法名和对应的类型)而导致每一种接口类型都不同,所以通常用type关键字重命名

定义接口

// 关键字 interface
type DuckInterface interface {
	run()  // 接口规定的方法,包括形参,返回值
	speak()
}

声明接口变量

接口是引用类型,即接口变量的零值是nil

// 基于定义好的接口类型声明一个接口变量
var d DuckInterface

// 接口变量的值,可以是实现了这个接口的任意类型的变量

实现

只要一个对象绑定了接口规定的所有方法,那么就表明该对象实现了该接口

// 以结构体为例

// 声明一个接口类型
type StudentInterface interface {
	learn()
	write()
}

// 声明两个结构体类型
type A struct {
	name string
	age  int
}

type B struct {
	name  string
	age   int
	hobby string
}


// 给结构体绑定方法,实现接口

// 指针接收器
func (s *A) write() {
	fmt.Println(s.name, "开始写作业")
}

func (s *A) learn() {
	fmt.Println(s.name, "开始学习")
}

// 值接收器
func (s B) write() {
	fmt.Println(s.name, "开始写作业")
}

func (s B) learn() {
	fmt.Println(s.name, "开始学习")
}

接口值

类型A实现了接口I,那么类型A的变量a就是一个I接口,它可以被赋值给接口I的变量i

1、变量a可以访问它本身的所有的属性和方法

2、a被赋值给i后,i就可以调用A类型实现接口的方法,但是不能访问a的其他方法和属性

3、i数据结构interface实现原理

注意:

A绑定接口方法时,若是使用指针接收器绑定,那么i就只能赋值为a的指针;

若是使用值接收器绑定接口方法,那么i既可以赋值为a,也可以赋值为a的指针

// 借助上一段代码进行演示
func main() {
	var a = A{"小红", 19}
	var b = B{"小刘", 19, "daPao",}
	var i StudentInterface

	i = &a  // 只能接收&a
    i.learn()
    
	i = b  // 可以接收b,也可以接收&b
	i.learn()
}

类型断言

接口i赋值为&a,&a就叫做接口i的动态值

通过i.(T),可以判断i的动态值的类型是不是T,若是,会返回动态值,否则就会抛出panic异常,这就是接口的类型断言

// 借助上一段代码进行演示
func main() {
	var a = A{"小红", 19}
	// var b = B{"小刘", 19, "daPao",}
	var i StudentInterface

	i = &a  
    
    a1 := i.(*A)  // 断言i接收的是一个A类型的变量:正确
    fmt.Println(a)
    b := i.(B)  // 断言i接收的是一个B类型的变量:错误,程序会报错
}

类型选择

i.(type) 直接得到接口i的动态值

这种语法,i只能是接口变量,并且只能在switch语句中使用

switch中,case会做对接口i的动态值的类型验证,验证通过就执行case代码

func testInterface() {
	//var a = A{"小红", 19}
	var b = B{"小刘", 19, "daPao",}
	var i StudentInterface
    
    // i可以接收b,&b和a,但是不能接收a
	i = &b 
    
	switch v := i.(type) {  // 此处v就是&b
	case *A:
		fmt.Printf("i接收的是一个%T类型的数据,%v", v,*v)
	case B:
		fmt.Printf("i接收的是一个%T类型的数据,%v",v,v)
	case *B:
		fmt.Printf("i接收的是一个%T类型的数据,%v",v,*v)
	default:
		fmt.Printf("i接收的是一个未知类型%T的数据,%v",v,v)
	}
	fmt.Println(b)
}

空接口与匿名空接口

空接口

// 接口内没有定义任何方法
type StudentInterface interface {}


// 所有的数据类型都实现了空接口:空接口变量可以接受任意类型的数据
var a StudentInterface
a = "hello"
a = 123
a = [3]int{1,2,3}

匿名接口

// 即不再使用type关键之命名接口类型,直接使用关键字interface声明一个接口变量

var i1 interface{}
var i2 interface{
    speak()
    learn()
}
i3 := interface{
    walk()
}

匿名空接口

var i interface{}

// i可以接收任意类型的变量

接口嵌套

声明

// 即type定义一个接口时,除了定义接口方法外,也可以定义一个接口类型

type PeopleInterface interface{
    speak()
    walk()
}

type StudentInterface {
    learn()
    write()
    PeopleInterface  // 嵌套了接口PeopleInterface
}
// StudentInterface相当于
type StudentInterface {
    learn()
    write()
    speak()
    walk()
}

断言接口

i.(T)判断接口i的动态值是不是实现了接口类型T

与断言接口动态值的数据类型不同:

1、进行断言的接口类型T只要是个接口类型即可,不一定要在接口i的接口类型中嵌套过

2、会返回两个值:

断言成功,第一个值为接口类型T的一个变量(它的动态值仍是接口i的动态值),第二个值为布尔值true;

断言失败,第一个值为接口类型的零值nil,第二个值为布尔值false

func testInterface(){
    //var a = A{"小红", 19}
	var b = B{"小刘", 19, "daPao",}
	var i StudentInterface
	i = &b
	
	p, ok := i.(PeopleInterface)
	fmt.Println(p,ok)  // p是一个接口,动态值为&b
}

非侵入式

嵌套在内的接口相当于面向对象中的父类

侵入式接口和非侵入式接口
侵入式接口:修改,删除,对子类有影响,如java
非侵入式接口:修改,删除,对子类有没有影响,如go,Python

posted @ 2020-03-28 10:56  DongDon  阅读(60)  评论(0)    收藏  举报