golang interface判断为空nil的实现代码


Posted in Golang onApril 24, 2021

要判断interface 空的问题,首先看下其底层实现。

interface 底层结构

根据 interface 是否包含有 method,底层实现上用两种 struct 来表示:iface 和 eface。eface表示不含 method 的 interface 结构,或者叫 empty interface。

对于 Golang 中的大部分数据类型都可以抽象出来 _type 结构,同时针对不同的类型还会有一些其他信息。

1.eface

type eface struct {
    _type *_type
    data  unsafe.Pointer
}
type _type struct {
    size       uintptr // type size
    ptrdata    uintptr // size of memory prefix holding all pointers
    hash       uint32  // hash of type; avoids computation in hash tables
    tflag      tflag   // extra type information flags
    align      uint8   // alignment of variable with this type
    fieldalign uint8   // alignment of struct field with this type
    kind       uint8   // enumeration for C
    alg        *typeAlg  // algorithm table
    gcdata    *byte    // garbage collection data
    str       nameOff  // string form
    ptrToThis typeOff  // type for pointer to this type, may be zero
}

2.iface

iface 表示 non-empty interface 的底层实现。相比于 empty interface,non-empty 要包含一些 method。method 的具体实现存放在 itab.fun 变量里。如果 interface 包含多个 method,这里只有一个 fun 变量怎么存呢?这个下面再细说。

type iface struct {
    tab  *itab
    data unsafe.Pointer
}
// layout of Itab known to compilers
// allocated in non-garbage-collected memory
// Needs to be in sync with
// ../cmd/compile/internal/gc/reflect.go:/^func.dumptypestructs.
type itab struct {
    inter  *interfacetype
    _type  *_type
    link   *itab
    bad    int32
    inhash int32      // has this itab been added to hash?
    fun    [1]uintptr // variable sized
}

概括起来,接口对象由接口表 (interface table) 指针和数据指针组成,或者说由动态类型和动态值组成。

struct Iface
{
    Itab* tab;
    void* data;
};
struct Itab
{
    InterfaceType* inter;
    Type* type;
    void (*fun[])(void);
};

接口表存储元数据信息,包括接口类型、动态类型,以及实现接口的方法指针。无论是反射还是通过接口调用方法,都会用到这些信息。

再来看下nil的定义。

nil的定义

// nil is a predeclared identifier representing the zero value for a pointer, channel, func, interface, map, or slice type.

var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

也就是说,只有pointer, channel, func, interface, map, or slice 这些类型的值才可以是nil.

如何判定interface里面的动态值是否空

对于一个接口的零值就是它的类型和值的部分都是nil。

一个接口值基于它的动态类型被描述为空或非空。

例如,

var w io.Writer

一般情况下,通过使用w==nil或者w!=nil来判读接口值是否为空,只是判断了动态类型,而没有判断动态值。

例如,下面的例子。

package main
import ("fmt")
func main(){
       var a interface{} = nil // tab = nil, data = nil
       var b interface{} = (*int)(nil) // tab 包含 *int 类型信息, data = nil
       fmt.Println(a==nil)
       fmt.Println(b==nil)
}

output:

true

false

上面代码中,接口b的动态类型为*int, 而动态值为nil,直接使用等于号无法判断。

所以不能直接通过与nil比较的方式判断动态值是否为空。

那如何判断动态值是否为空?

可以借助反射来判断。

func IsNil(i interface{}) bool {
    defer func() {
        recover()
    }()
    vi := reflect.ValueOf(i)
    return vi.IsNil()
}

其中,IsNil定义如下:

func (v Value) IsNil() bool

参数v必须是chan, func, interface, map, pointer, or slice,否则会panic。

如果调用IsNil的不是一个指针,会出现异常,需要捕获异常。

或者修改成这样:

func IsNil(i interface{}) bool {
    vi := reflect.ValueOf(i)
    if vi.Kind() == reflect.Ptr {
        return vi.IsNil()
    }
    return false
}

总结

一个接口包括动态类型和动态值。

如果一个接口的动态类型和动态值都为空,则这个接口为空的。

补充:golang返回值为interface{}的类型判断

看标题就知道,这是一个很简单的问题,就一个函数的事,但是,今天一同学golang的几个人中,已经不止一个人问我了,在这里我就说一下,也希望对不清楚的娃有些许帮助,大神别喷,飘过就行了。

大家知道,golang对于不确定返回值可以用interface{}代替,这确实很方便,但是也带来了问题,那就是如何判断返回值是什么类型的?其实可以用反射也就是reflect来判断,通过函数

reflect.TypeOf()

即返回类型!

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Golang 相关文章推荐
用golang如何替换某个文件中的字符串
Apr 25 Golang
浅谈Golang 嵌套 interface 的赋值问题
Apr 29 Golang
golang slice元素去重操作
Apr 30 Golang
解决golang 关于全局变量的坑
May 06 Golang
Go语言实现Base64、Base58编码与解码
Jul 26 Golang
Go语言实现一个简单的并发聊天室的项目实战
Mar 18 Golang
详解Golang如何优雅的终止一个服务
Mar 21 Golang
Go归并排序算法的实现方法
Apr 06 Golang
Golang 对es的操作实例
Apr 20 Golang
Go语言怎么使用变长参数函数
Jul 15 Golang
Go语言测试库testify使用学习
Jul 23 Golang
GO中sync包自由控制并发示例详解
Aug 05 Golang
golang判断key是否在map中的代码
Apr 24 #Golang
Go语言操作数据库及其常规操作的示例代码
Apr 21 #Golang
为什么不建议在go项目中使用init()
Apr 12 #Golang
Golang二维切片初始化的实现
Apr 08 #Golang
go语言map与string的相互转换的实现
Apr 07 #Golang
一文读懂go中semaphore(信号量)源码
Apr 03 #Golang
Go Gin实现文件上传下载的示例代码
Apr 02 #Golang
You might like
Yii框架实现多数据库配置和操作的方法
2017/05/25 PHP
Laravel框架中Blade模板的用法示例
2017/08/30 PHP
用jquery ajax获取网站Alexa排名的代码
2009/12/12 Javascript
在chrome浏览器中,防止input[text]和textarea在聚焦时出现黄色边框的解决方法
2011/05/24 Javascript
jquery遍历checkbox的注意事项说明
2014/02/21 Javascript
jQuery实现自定义右键菜单的树状菜单效果
2015/09/02 Javascript
JS实现漂亮的窗口拖拽效果(可改变大小、最大化、最小化、关闭)
2015/10/10 Javascript
jQuery点击弹出层弹出模态框点击模态框消失代码分享
2017/01/21 Javascript
Vue利用路由钩子token过期后跳转到登录页的实例
2017/10/26 Javascript
详解基于Vue,Nginx的前后端不分离部署教程
2018/12/04 Javascript
vue实现移动端图片上传功能
2019/12/23 Javascript
浅析JavaScript中的事件委托机制跟深浅拷贝
2021/01/20 Javascript
python判断端口是否打开的实现代码
2013/02/10 Python
python模拟登录百度代码分享(获取百度贴吧等级)
2013/12/27 Python
python操作数据库之sqlite3打开数据库、删除、修改示例
2014/03/13 Python
Python中动态获取对象的属性和方法的教程
2015/04/09 Python
介绍Python中的文档测试模块
2015/04/28 Python
深入理解 Python 中的多线程 新手必看
2016/11/20 Python
利用Django内置的认证视图实现用户密码重置功能详解
2017/11/24 Python
python通过getopt模块如何获取执行的命令参数详解
2017/12/29 Python
Python+matplotlib+numpy实现在不同平面的二维条形图
2018/01/02 Python
python pycharm最新版本激活码(永久有效)附python安装教程
2020/09/18 Python
Python连接SQLite数据库并进行增册改查操作方法详解
2020/02/18 Python
用opencv给图片换背景色的示例代码
2020/07/08 Python
python中Mako库实例用法
2020/12/31 Python
苏宁红孩子母婴商城:redbaby
2017/02/12 全球购物
英国领先的电动可调床制造商:Laybrook
2019/12/26 全球购物
中东最大的在线宠物店:Dubai Pet Food
2020/06/11 全球购物
2014年世界艾滋病日宣传活动总结
2014/11/18 职场文书
农业项目合作意向书
2015/05/08 职场文书
纪委立案决定书
2015/06/24 职场文书
广播体操比赛主持词
2015/06/29 职场文书
创业开店,这样方式更合理
2019/08/26 职场文书
手把手教你怎么用Python实现zip文件密码的破解
2021/05/27 Python
Python异常类型以及处理方法汇总
2021/06/05 Python
自动在Windows中运行Python脚本并定时触发功能实现
2021/09/04 Python