• golang学习笔记---空接口


    空接口是指没有定义任何接口方法的接口。没有定义任何接口方法,意味着Go中的任意对象都可以实现空接口(因为没方法需要实现),任意对象都可以保存到空接口实例变量中

    空接口的定义方式:

    type empty_int interface {
    }

    通常会简写为type empty_int interface{}

    更常见的,会直接使用interface{}作为一种类型,表示空接口。例如:

    // 声明一个空接口实例
    var i interface{}
    

      

    再比如函数使用空接口类型参数:

    func myfunc(i interface{})

    在Go中很多地方都使用空接口类型的参数,用的最多的fmt中的Print类方法:

    
    $ go doc fmt Println
    func Println(a ...interface{}) (n int, err error)
    

    空接口数据结构

    可以定义一个空接口类型的array、slice、map、struct等,这样它们就可以用来存放任意类型的对象,因为任意类型都实现了空接口。

    例如,创建一个空接口的slice:

    package main
    
    import "fmt"
    
    func main() {
    	any := make([]interface{}, 5)
    	any[0] = 11
    	any[1] = "hello world"
    	any[2] = []int{11, 22, 33, 44}
    	for _, value := range any {
    		fmt.Println(value)
    	}
    }
    

      

    输出结果:

    11
    hello world
    [11 22 33 44]
    <nil>
    <nil>
    

    显然,通过空接口类型,Go也能像其它动态语言一样,在数据结构中存储任意类型的数据。

    再比如,某个struct中,如果有一个字段想存储任意类型的数据,就可以将这个字段的类型设置为空接口:

    type my_struct struct {
    	anything interface{}
    	anythings []interface{}
    }
    

      

    拷贝数据结构到空接口数据结构

    前面解释了任意类型的对象都能赋值给空接口实例。

    var any interface{}
    any = "hello world"
    any = 11
    

      

    空接口是一种接口,它是一种指针类型的数据类型,虽然不严谨,但它确实保存了两个指针,一个是对象的类型(或iTable),一个是对象的值。所以上面的赋值过程是让空接口any保存各个数据对象的类型和对象的值。

    换一种角度考虑,空接口有自己的内存布局方式:两个指针,占用两个机器字长。

    Golang给的一个经典的示例:将某个slice中的数据拷贝到空接口slice中将报错。

    package main
    
    import "fmt"
    
    func main() {
    	testSlice := []int{11,22,33,44}
    
    	// 成功拷贝
    	var newSlice []int
    	newSlice = testSlice
    	fmt.Println(newSlice)
    
    	// 拷贝失败
    	var any []interface{}
    	any = testSlice
    	fmt.Println(any)
    }
    

      

    这是因为每个空接口的内存布局都占用两个机器字长的内容。对于长度为N的空接口slice来说,它的每个元素都是以2机器字长为单元的连续空间,共占用N*2个机器字长的空间。

    而普通的slice,例如上面的testSlice,它的每个元素是int类型的,int类型的内存布局和空接口不一样。

    这些对象的内存布局在编译期间就已经确定好了,所以没法直接将不同内存布局的数据结构进行拷贝。

    要想完成期待的拷贝,可以使用for-range的方式,将testSlice中的每个元素赋值给空接口slice的空接口元素:也就是一个个的空接口实例。

    var any []interface{}
    for _,value := range testSlice{
    	any = append(any,value)
    }

    完整示例:
    package main
    
    import "fmt"
    
    func main() {
    	testSlice := []int{11, 22, 33, 44}
    
    	// 成功拷贝
    	var newSlice []int
    	newSlice = testSlice
    	fmt.Println(newSlice)
    
    	var any []interface{}
    	for _, value := range testSlice {
    		any = append(any, value)
    	}
    	fmt.Println(any)
    }
    

      输出:

    [11 22 33 44]

    [11 22 33 44]

    这样,空接口Slice中的每个空接口实例都指向更底层的各个数据对象。而不是像前面错误的拷贝方式:每个空接口元素想要当作这些数据对象。

    不仅空接口的Slice如此,其它包含空接口的数据结构,也都类似。

  • 相关阅读:
    加入强调语气,使用<strong>和<em>标签
    了解<hx>标签,为你的网页添加标题
    开始学习<p>标签,添加段落
    <body>标签,网页上显示的内容放在这里
    语义化,让你的网页更好的被搜索引擎理解
    深入源码分析SpringMVC底层原理(二)
    设计模式是什么鬼(解释器)
    Java并发面试题
    漫画 | Spring AOP的底层原理是什么?
    漫画|你还记得原生的JDBC怎么连接数据库吗?
  • 原文地址:https://www.cnblogs.com/saryli/p/13273736.html
Copyright © 2020-2023  润新知