go语言接口与断言

Posted zpf253

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go语言接口与断言相关的知识,希望对你有一定的参考价值。

go语言接口与断言

类型

接口也是go语言中的一种类型,它能够出现在变量的定义、函数的入参和返回值中并对它们进行约束,不过go语言中有两种略微不同的接口,一种是带有一组方法的接口,另一种是不带任何方法的interface

go语言使用runtime.iface表示第一种接口,使用runtime.eface表示第二种不包含任何方法的interface,两种接口虽然都是用interface声明,但是由于后者在go语言中很常见,所以在实现时使用了特殊类型。

数据结构

// 没有方法的interface
type eface struct 
	_type *_type
	data  unsafe.Pointer


// 记录着Go语言中某个数据类型的基本特征
type _type struct 
	size       uintptr
	ptrdata    uintptr // size of memory prefix holding all pointers
	hash       uint32
	tflag      tflag
	align      uint8
	fieldAlign uint8
	kind       uint8
	// function for comparing objects of this type
	// (ptr to object A, ptr to object B) -> ==?
	equal func(unsafe.Pointer, unsafe.Pointer) bool
	// gcdata stores the GC type data for the garbage collector.
	// If the KindGCProg bit is set in kind, gcdata is a GC program.
	// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
	gcdata    *byte
	str       nameOff
	ptrToThis typeOff


// 有方法的interface
type iface struct 
	tab  *itab
	data unsafe.Pointer


type itab struct 
	inter *interfacetype
	_type *_type
	hash  uint32 // copy of _type.hash. Used for type switches.
	_     [4]byte
	fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.



// interface数据类型对应的type
type interfacetype struct 
	typ     _type
	pkgpath name
	mhdr    []imethod


eface结构体只包含指向底层数据和类型的两个指针,iface结构体有指向原始数据的指针data,但更重要的是runtime.itab类型的tab字段。

类型结构体

runtime._type是go语言类型的运行时表示。其中包含了很多类型的元信息,例如类型的大小、哈希、对齐以及种类。

  • size字段存储了类型占用的内存空间,为内存空间的分配提供信息。
  • hash字段能够帮助我们快速确定类型是否相等。

itab结构体

runtime.itab结构体是接口类型的核心组成部分,每一个runtime.itab都占32字节。我们可以将其看成接口类型和具体类型的组合,他们分别用inner和_type两个字段表示。

除inner和_type这两个用于表示类型的字段外,上述结构体中的另外两个字段也有自己的作用。

  • hash是对_type.hash的复制,当我们想将interface类型转换成具体类型时,可以使用该字段快速判断目标类型和具体类型runtime._type是否一致。
  • fun是一个动态大小的数组,它是一个用于动态派发的虚函数表,存储了一组函数指针。虽然该变量被声明成大小固定的数组,但在使用时会通过原始指针获取其中的数据,所以fun数组中保存的元素数量是不确定的。

go语言接口断言

接口断言

因为空接口 interface{}没有定义任何函数,因此 Go 中所有类型都实现了空接口。当一个函数的形参是interface{},那么在函数中,需要对形参进行断言,从而得到它的真实类型。

语法格式:

// 安全类型断言

<目标类型的值>,<布尔参数> := <表达式>.( 目标类型 )

//非安全类型断言

<目标类型的值> := <表达式>.( 目标类型 )

示例代码:

package main

import "fmt"

func main() {

   var i1 interface{} = new (Student)
   s := i1.(Student) //不安全,如果断言失败,会直接panic

   fmt.Println(s)


	var i2 interface{} = new(Student)
	s, ok := i2.(Student) //安全,断言失败,也不会panic,只是ok的值为false
	if ok {
		fmt.Println(s)
	}
}

type Student struct {

}

断言其实还有另一种形式,就是用在利用 switch语句判断接口的类型。每一个case会被顺序地考虑。当命中一个case 时,就会执行 case 中的语句,因此 case 语句的顺序是很重要的,因为很有可能会有多个 case匹配的情况。

示例代码:

switch ins:=s.(type) {
	case Triangle:
		fmt.Println("三角形。。。",ins.a,ins.b,ins.c)
	case Circle:
		fmt.Println("圆形。。。。",ins.radius)
	case int:
		fmt.Println("整型数据。。")
	}

以上是关于go语言接口与断言的主要内容,如果未能解决你的问题,请参考以下文章

go语言接口(详解)

go语言学习笔记 — 进阶 — 接口:在接口和类型之间转换

Go——空接口与断言

go语言基础包,接口

Go-动态类型与类型断言详解(含type-switch及全部代码)

接口——定义,实现接口的条件,类型与接口的关系,类型断言