什么网站可以做ui小动画,wordpress多站点必备插件,外包服务合同,企业网站的运营如何做理解Go Interface1 概述Go语言中的接口很特别#xff0c;而且提供了难以置信的一系列灵活性和抽象性。接口是一个自定义类型#xff0c;它是一组方法的集合#xff0c;要有方法为接口类型就被认为是该接口。从定义上来看#xff0c;接口有两个特点:接口本质是一种自定义类型…理解Go Interface1 概述Go语言中的接口很特别而且提供了难以置信的一系列灵活性和抽象性。接口是一个自定义类型它是一组方法的集合要有方法为接口类型就被认为是该接口。从定义上来看接口有两个特点:接口本质是一种自定义类型因此不要将Go语言中的接口简单理解为C/Java中的接口后者仅用于声明方法签名。接口是一种特殊的自定义类型其中没有数据成员只有方法也可以为空。接口是完全抽象的因此不能将其实例化。然而可以创建一个其类型为接口的变量它可以被赋值为任何满足该接口类型的实际类型的值。接口的重要特性是只要某个类型实现了接口所有的方法那么我们就说该类型实现了此接口。该类型的值可以赋给该接口的值。作为1的推论任何类型的值都可以赋值给空接口interface{}。接口的特性是Go语言支持鸭子类型的基础即“如果它走起来像鸭子叫起来像鸭子实现了接口要的方法它就是一只鸭子可以被赋值给接口的值”。凭借接口机制和鸭子类型Go语言提供了一种有利于类、继承、模板之外的更加灵活强大的选择。只要类型T的公开方法完全满足接口I的要求就可以把类型T的对象用在需要接口I的地方。这种做法的学名叫做”Structural Typing“。2 方法Go语言中同时有函数和方法。一个方法就是一个包含了接受者的函数接受者可以是命名类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。type User struct {Name stringEmail string
}func (u User) Notify() error// User 类型的值可以调用接受者是值的方法
damon : User{AriesDevil, ariesdevilxxoo.com}
damon.Notify()// User 类型的指针同样可以调用接受者是值的方法
alimon : User{A-limon, alimonooxx.com}
alimon.Notify()
User的结构体类型定义了一个该类型的方法叫做Notify该方法的接受者是一个User类型的值。要调用Notify方法我们需要一个 User类型的值或者指针。Go调用和解引用指针使得调用可以被执行。注意当接受者不是一个指针时该方法操作对应接受者的值的副本(意思就是即使你使用了指针调用函数但是函数的接受者是值类型所以函数内部操作还是对副本的操作而不是指针操作。我们可以修改Notify方法让它的接受者使用指针类型func (u *User) Notify() error
再来一次之前的调用(注意当接受者是指针时即使用值类型调用那么函数内部也是对指针的操作。总结一个结构体的方法的接收者可能是类型值或指针如果接收者是值无论调用者是类型值还是类型指针修改都是值的副本如果接收者是指针则调用者修改的是指针指向的值本身。3 接口实现type Notifier interface {Notify() error
}func SendNotification(notify Notifier) error {return notify.Notify()
}unc (u *User) Notify() error {log.Printf(User: Sending User Email To %s%s\n,u.Name,u.Email)return nil
}func main() {user : User{Name: AriesDevil,Email: ariesdevilxxoo.com,}SendNotification(user)
}// Output:
cannot use user (type User) as type Notifier in function argument:
User does not implement Notifier (Notify method has pointer receiver)
上述代码是编译不过的见Output编译错误关键信息Notify method has pointer receiver。 编译器不考虑我们的值是实现该接口的类型接口的调用规则是建立在这些方法的接受者和接口如何被调用的基础上。下面的是语言规范里定义的规则这些规则用来说明是否我们一个类型的值或者指针实现了该接口类型 *T 的可调用方法集包含接受者为 *T 或 T 的所有方法集类型 T 的可调用方法集包含接受者为 T 的所有方法类型 T 的可调用方法集不包含接受者为 *T 的方法也就是说接收者是指针 *T 时接口的实例必须是指针接收者是值 T 时接口的实例可以是指针也可以是值4 空接口与nil空接口(interface{})不包含任何的method正因为如此所有的类型都实现了interface{}。interface{}对于描述起不到任何的作用(因为它不包含任何的method但是interface{}在我们需要存储任意类型的数值的时候相当有用因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。Go语言中的nil在概念上和其它语言的null、None、nil、NULL一样都指代零值或空值。nil是预先说明的标识符也即通常意义上的关键字。nil只能赋值给指针、channel、func、interface、map或slice类型的变量。如果未遵循这个规则则会引发panic。在底层interface作为两个成员来实现一个类型(type)和一个值(data)。参考官方文档翻译Go中error类型的nil值和nil。import (fmtreflect
)func main() {var val interface{} int64(58)fmt.Println(reflect.TypeOf(val))val 50fmt.Println(reflect.TypeOf(val))
}
type用于存储变量的动态类型data用于存储变量的具体数据。在上面的例子中第一条打印语句输出的是int64。这是因为已经显示的将类型为int64的数据58赋值给了interface类型的变量val所以val的底层结构应该是(int64, 58)。我们暂且用这种二元组的方式来描述二元组的第一个成员为type第二个成员为data。第二条打印语句输出的是int。这是因为字面量的整数在golang中默认的类型是int所以这个时候val的底层结构就变成了(int, 50)。func main() {var val interface{} nilif val nil {fmt.Println(val is nil)} else {fmt.Println(val is not nil)}
}
变量val是interface类型它的底层结构必然是(type, data)。由于nil是untyped(无类型)而又将nil赋值给了变量val所以val实际上存储的是(nil, nil)。因此很容易就知道val和nil的相等比较是为true的。进一步验证func main() {var val interface{} (*interface{})(nil)if val nil {fmt.Println(val is nil)} else {fmt.Println(val is not nil)}
}
(*interface{})(nil)是将nil转成interface类型的指针其实得到的结果仅仅是空接口类型指针并且它指向无效的地址。也就是空接口类型指针而不是空指针这两者的区别蛮大的。对于(*int)(nil)、(*byte)(nil)等等来说是一样的。上面的代码定义了接口指针类型变量val它指向无效的地址(0x0)因此val持有无效的数据。但它是有类型的(*interface{})。所以val的底层结构应该是(*interface{}, nil)。有时候您会看到(*interface{})(nil)的应用比如var ptrIface (*interface{})(nil)如果您接下来将ptrIface指向其它类型的指针将通不过编译。或者您这样赋值*ptrIface 123那样的话编译是通过了但在运行时还是会panic的这是因为ptrIface指向的是无效的内存地址。其实声明类似ptrIface这样的变量是因为使用者只是关心指针的类型而忽略它存储的值是什么。小结: 无论该指针的值是什么(*interface{}, nil)这样的接口值总是非nil的即使在该指针的内部为nil。5 接口变量存储的类型接口的变量里面可以存储任意类型的数值(该类型实现了某interface)。那么我们怎么反向知道这个变量里面实际保存了的是哪个类型的对象呢目前常用的有两种方法comma-ok断言value, ok element.(T)这里value就是变量的值ok是一个bool类型element是interface变量T是断言的类型。如果element里面确实存储了T类型的数值那么ok返回true否则返回false。switch测试switch value : element.(type) {case int:fmt.Printf(list[%d] is an int and its value is %d\n, index, value)case string:fmt.Printf(list[%d] is a string and its value is %s\n, index, value)...
element.(type)语法不能在switch外的任何逻辑里面使用如果你要在switch外面判断一个类型就使用comma-ok。6 接口与反射反射是程序运行时检查其所拥有的结构尤其是类型的一种能力。Go语言也提供对反射的支持。在前面的interface{}与nil的底层实现已提到在reflect包中有两个类型需要了解Type和Value。这两个类型使得可以访问接口变量的内容还有两个简单的函数reflect.TypeOf和reflect.ValueOf从接口值中分别获取reflect.Type 和reflect.Value。如同物理中的反射在Go语言中的反射也存在它自己的镜像。从reflect.Value可以使用Interface方法还原接口值:var x float64 3.4
v : reflect.ValueOf(x)// Interface 以 interface{} 返回 v 的值。
// func (v Value) Interface() interface{}// y 将为类型 float64
y : v.Interface().(float64)
fmt.Println(y)
声明本文是收集网上一些关于Go语言中接口(interface)的说明是一篇学习笔记文中多处引用参考文章列表在最后可直接访问了解详情。参考[1] Go 语言中的方法接口和嵌入类型[2] 详解interface和nil[3] Go语言interface详解转载于:https://www.cnblogs.com/zhangboyu/p/7453003.html