浅谈Golang中的接口值
funnyx · · 3747 次点击 · · 开始浏览在golang中,接口值是由两部分组成的,一部分是接口的类型,另一部分是该类型对应的值,我们称其为动态类型和动态值。
这个概念该如何理解呢?我们先看一段代码:
var w io.Writer // type-<nil>
w = new(bytes.Buffer) // type-*bytes.Buffer
w = nil // type-<nil>
这里先定义一个变量w,然后再为其赋值,可以看到,变量w的type都是不太一样的,可以用fmt的%T来查看其动态类型。
fmt.Printf("%T\n",w)
在第一行定义变量w的时候,声明了其类型为io.Writer,这里是真正意义上的空接口,为什么是空接口,就是它的类型和值都为nil,在这里可以用==或者!=来和nil做判断。
w == nil // return true
在第二行为变量w赋值的时候,此时w的动态类型为*bytes.Buffer,然后动态值是一个指向新分配的缓冲区的指针。
package bytes
type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)]
bootstrap [64]byte // memory to hold first slice; helps small buffers avoid allocation.
lastRead readOp // last read operation, so that Unread* can work correctly.
}
此时就可以调用Writer接口中的方法:
w.Write([]byte("ok"))
顺便提一下,如果用第一行中的变量w来调用Write方法的话,程序会报错,调用一个空接口值上的任意方法都会产生Panic。
第三行为w赋值的效果就和最初是一样的了,动态类型和动态值都是nil,为一个空接口。
说到这里,大概能明白接口值的意义了,不过还有一个问题,那就是一个接口为空和一个接口包含空指针是否是一回事?我们来看一段代码:
func test(w io.Writer) {
if w != nil{
w.Write([]byte("ok"))
}
}
func main() {
var buf *bytes.Buffer
test(buf)
}
如果执行这段程序,回报错,我们来稍微分析一下,在main()中,我们首先声明了一个buf变量,类型是*bytes.Buffer指针类型,在调用函数test()的时候,参数w会被赋值为动态类型为*bytes.Buffer,动态值为nil,也就是w是一个包含了空指针值的非空接口。那么在w != nil判断时,这个等式便是成立的,不过这里也从侧面反映出一个现象,就是这种传参都是值拷贝,那么看到这里,这段代码也应该比较好修改了:
var buf io.Writer // buf = new(bytes.Buffer)
我们再来看一段代码:
type Test interface {}
type Test1 interface {
TestFunc()
}
type Structure struct {
a int
}
func (s *Structure) TestFunc(){
fmt.Println("Ok, Let's rock and roll!")
}
func fTest(t Test) {
fmt.Println(t == nil)
}
func fTest1(t1 Test1){
fmt.Println(t1 == nil)
}
func fStructure(s *Structure){
fmt.Println(s == nil)
}
func main() {
var s *Structure = nil
fTest(s) // false
fTest1(s) // false
fStructure(s) // true
s.TestFunc() // Ok, Let's rock and roll!
}
执行一下代码,是否和预期的结果一样呢?Ok, Let's rock and roll!
有疑问加站长微信联系(非本文作者)
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
关注微信- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码` - 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传
收入到我管理的专栏 新建专栏
在golang中,接口值是由两部分组成的,一部分是接口的类型,另一部分是该类型对应的值,我们称其为动态类型和动态值。
这个概念该如何理解呢?我们先看一段代码:
var w io.Writer // type-<nil>
w = new(bytes.Buffer) // type-*bytes.Buffer
w = nil // type-<nil>
这里先定义一个变量w,然后再为其赋值,可以看到,变量w的type都是不太一样的,可以用fmt的%T来查看其动态类型。
fmt.Printf("%T\n",w)
在第一行定义变量w的时候,声明了其类型为io.Writer,这里是真正意义上的空接口,为什么是空接口,就是它的类型和值都为nil,在这里可以用==或者!=来和nil做判断。
w == nil // return true
在第二行为变量w赋值的时候,此时w的动态类型为*bytes.Buffer,然后动态值是一个指向新分配的缓冲区的指针。
package bytes
type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)]
bootstrap [64]byte // memory to hold first slice; helps small buffers avoid allocation.
lastRead readOp // last read operation, so that Unread* can work correctly.
}
此时就可以调用Writer接口中的方法:
w.Write([]byte("ok"))
顺便提一下,如果用第一行中的变量w来调用Write方法的话,程序会报错,调用一个空接口值上的任意方法都会产生Panic。
第三行为w赋值的效果就和最初是一样的了,动态类型和动态值都是nil,为一个空接口。
说到这里,大概能明白接口值的意义了,不过还有一个问题,那就是一个接口为空和一个接口包含空指针是否是一回事?我们来看一段代码:
func test(w io.Writer) {
if w != nil{
w.Write([]byte("ok"))
}
}
func main() {
var buf *bytes.Buffer
test(buf)
}
如果执行这段程序,回报错,我们来稍微分析一下,在main()中,我们首先声明了一个buf变量,类型是*bytes.Buffer指针类型,在调用函数test()的时候,参数w会被赋值为动态类型为*bytes.Buffer,动态值为nil,也就是w是一个包含了空指针值的非空接口。那么在w != nil判断时,这个等式便是成立的,不过这里也从侧面反映出一个现象,就是这种传参都是值拷贝,那么看到这里,这段代码也应该比较好修改了:
var buf io.Writer // buf = new(bytes.Buffer)
我们再来看一段代码:
type Test interface {}
type Test1 interface {
TestFunc()
}
type Structure struct {
a int
}
func (s *Structure) TestFunc(){
fmt.Println("Ok, Let's rock and roll!")
}
func fTest(t Test) {
fmt.Println(t == nil)
}
func fTest1(t1 Test1){
fmt.Println(t1 == nil)
}
func fStructure(s *Structure){
fmt.Println(s == nil)
}
func main() {
var s *Structure = nil
fTest(s) // false
fTest1(s) // false
fStructure(s) // true
s.TestFunc() // Ok, Let's rock and roll!
}
执行一下代码,是否和预期的结果一样呢?Ok, Let's rock and roll!