1、interface{}
没有任何方法的接口就是空接口,实际上每个类型都实现了空接口,所以空接口类型可以接受任何类型的数据。
// 定义一个空接口
type phone interface{}
// 空接口作为参数,传进来任意类型参数判断其类型与打印其值
func showmpType(q interface{}) {
fmt.Printf("type:%T,value:%vn", q, q)
}
interface{}
不包含任何方法,正因为如此,所有的类型都实现了interface{}
。interface{}
看起来好像没什么作用,但是它可用来存储任意类型的值。它有点类似C语言的void *类型。
package main
import "fmt"
func main() {
var a interface{}
var i int = 100
s := "Hello World"
// a可以存储任意类型的值
a = i
fmt.Println(a)
a = s
fmt.Println(a)
}
2、接口值的内存结构
从概念上来讲,一个接口类型的值(简称接口值)其实有两个部分:一个具体的类型和该类型的一个值。
在我们的概念模型中,用类型描述符来提供每个类型的具体信息,比如它的名字和方法集。对于一个接口值,类型部分就是用类型描述符来表述。接口值得内存布局如下所示:
我们首先来看一下接口的零值。在Go语言中,变量默认初始化为其零值。接口的零值就是把它的动态类型和动态值都设置为nil。
一个接口值是否为nil取决于它的动态类型,所以接口的零值是一个nil接口值,因为它的动态类型是nil。
可以通过== nil
或者 != nil
来判断接口值是否为nil。调用一个nil接口值的任何方法都会导致崩溃。
var s Shape
s.GetArea() // 崩溃:s是nil接口值
我们再来看一下接口值是某类型的一个实例的情形,我们以之前示例代码中的的Circle类型为例,当把一个Circle实例赋值给接口变量时,接口值的内存布局如下:
可以看出,接口值的动态类型是Circle类型,接口值的动态值是一个指向Circle实例的指针。
我们再来看一下接口值是某类型的实例的指针时的情形,我们还是以示例代码种的Circle类型为例,当把一个Circle实例的指针赋值给接口变量时,接口值的内存布局如下:
可以看出,接口值的动态类型是*Circle类型(注意与前面的那种情形不同),接口值的动态值仍然是一个指向Circle实例的指针。
-
一般来讲,在编译时我们无法知道一个接口值的动态类型会是什么,所以通过接口来做调用必然需要使用动态分发。即,编辑器必须生成一段代码来从类型描述符拿到对应的方法地址,再间接的调用该方法地址。调用该方法的接口者就是接口值的动态值。
-
接口值可以用==和!=操作符来比较。如果两个接口值都是nil或者二者的动态类型完全一致且动态值都相等,那么两个接口值相等。
注意:比较两个接口时,如果两个接口值的动态类型一致,但对应的动态值是不可比较的(比如slice),那么这个比较在执行时会发生崩溃:
var x interface{} = []int{1, 2, 3}
fmt.Println(x == x) // 崩溃
当处理错误或者调试时,能拿到接口值得动态类型是很有帮助的。可以使用fmt包的%T来实现这个需求:
var w io.Writer
fmt.Printf("%Tn", w) // <nil>
w = os.Stdout
fmt.Printf("%Tn", w) // *os.File
w = new(bytes.Buffer)
fmt.Printf("%Tn", w) // *bytes.Buffer
3、接口的嵌套
// 接口类型可以进行嵌套
//定义总接口
type animal interface {
peo
dog
cat
}
//定义包含的接口
type peo interface {
}
type dog interface {
}
type cat interface {
}
原文地址:https://blog.csdn.net/fanjufei123456/article/details/129952856
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_33932.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!