注册 登录

清河洛

Go语言中的接口

qingheluo2021-09-08清河洛337
接口是一组方法的集合,定义了接口中存储的数据实例的行为任何数据实例只要全部实现了这些方法就是实现了这个接口既然接口定义了数据实例的行为,就意味着接口必须赋值一个数据实例,然后这个数据实例才能使用接口中的方法每一个接口都包含两个属性,一个是值,一个是类型定义接口type interface_name interface { method_name1 [return_type] method_name2 [return_type] ... method_namen [return_type] }以下为一个实现接口的实例type Human interface { ...

接口是一组方法的集合,定义了接口中存储的数据实例的行为

任何数据实例只要全部实现了这些方法就是实现了这个接口

既然接口定义了数据实例的行为,就意味着接口必须赋值一个数据实例,然后这个数据实例才能使用接口中的方法

每一个接口都包含两个属性,一个是值,一个是类型

定义接口

type interface_name interface {
   method_name1 [return_type]
   method_name2 [return_type]
   ...
   method_namen [return_type]
}
以下为一个实现接口的实例
type Human interface {
    eat(food string) string
    sleep()
}
//定义一个human接口

type People struct {
    name string
}
//定义一个结构体

func (who People) eat(food string) string {
    fmt.Println(me.name,"吃",food)
    return "吃饱了"
}
func (who People) sleep() {
    fmt.Println(me.name,"睡觉")
}
//实现接口所有方法并绑定到People结构体的who实例变量上

func main() {
    var me Human
    //声明一个要实现接口的变量
    me = People {name:"三千"}
    //给接口赋值,让值绑定接口
    //上面两句可以直接使用一句
    //var me Human = People {name:"三千"}
    fmt.Println(me.eat("面条"))
    me.sleep()
}

上面程序运行后会显示:
三千 吃 面条
吃饱了
三千 睡觉

空接口

空接口是特殊形式的接口类型,普通的接口都有方法,而空接口没有定义任何方法

可以说所有类型都至少实现了空接口,也就是说空接口可以承载任意类型的值

一个空接口的值和类型都是nil

使用空接口

直接使用 interface{} 作为类型声明一个承载任意类型值的变量

var demo interface{}

demo = 5
demo = "hello"
demo = true
一个空接口可以赋予任何类型的值

使用func demo (faces ...interface{})可以接受任意个任意类型的值

当空接口承载数组或切片后,该对象无法进行切片(arr[start_index:end_index])操作

断言类型(Type Assertion)

当使用空接口来接收任意类型的值时,它的静态类型是 interface{},但动态类型我们并不知道,因此需要使用类型断言(Type Assertion)来判断其动态类型,即其承载的数据的数据类型

类型断言仅能对静态类型为空接口(interface{})的对象进行,否则会抛出错误

断言类型的格式:val.(type_name)

会返回两个值,第一个是变量所存储的实际数据,第二个是断言类型的结果(bool值)

首先判断变量的值是否非nil
然后判断变量的值是否是type_name类型
两个条件都通过会返回变量的值和true
有一个条件不通过会返回type_name的默认值和false

断言类型失败时,如果有变量接收第二个返回值(false)则正常运行,如果没有变量接受会触发panic

var demo interface{} = "hello"
var a = demo.(string)
var b,ok = demo.(int)
var c = demo.(int)
变量a由于断言成功被赋值"hello"
变量b断言失败但是有ok变量接受false,所以会被赋值int默认值0
变量c初始化时由于断言失败而没有接收第二个返回值,触发panic

如果需要断言多种类型,使用type-switch流程控制语句比一个个进行类型断言更简单、直接、高效

var demo = interface{}("demo")

switch v := demo.(type){
    case int:
        fmt.Println("demo的类型是int")
    case string:
        fmt.Println("demo的类型是string")
    case nil:
        fmt.Println("demo的类型是空值nil")
     default:
         fmt.Printf("demo为未知类型")
}

静态类型和动态类型

静态类型(static type)

就是声明变量时的类型,是编码代码时就确定的

var age int      //  静态类型int
var name string  //  静态类型string
var demo interface{} // 静态类型interface{}

动态类型(concrete type)

也叫具体类型,是程序运行时系统才能看见的类型,是由空接口实现的

var demo interface{}

demo = 5
demo = "hello"

以上代码中在声明变量demo时指定了变量为interface{}类型,所以demo的静态类型就是interface{},这个是在编写代码的时候就确定且不会改变的。

后面我们又给demo变量赋值int类型5,此时它的静态类型还是interface{},这是不会变的,但是它的动态类型此时变成了int类型

同理,我们给变量赋值string类型"hello",此时它的静态类型还是interface{},它的动态类型此时又变成了string类型

无论是demo=5还是demo="hello",都是当程序运行到这里时,变量的类型,才发生了改变,也就是程序运行时才能确定的类型

由于动态类型的存在,在一个函数中接收的参数的类型有可能无法预先知晓,此时我们就要对参数进行反射,然后根据不同的类型做不同的处理



网址导航