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

interface 底层结构

根据 interface 是否包含有 method,底层实现上用两种 struct 来表示:iface 和 eface。eface表示不含 method 的 interface 结构,或者叫 empty interface。对于 Golang 中的大部分数据类型都可以抽象出来 _type 结构,同时针对不同的类型还会有一些其他信息。

1.eface

  1. type eface struct {
  2. _type *_type
  3. data unsafe.Pointer
  4. }
  5. type _type struct {
  6. size uintptr // type size
  7. ptrdata uintptr // size of memory prefix holding all pointers
  8. hash uint32 // hash of type; avoids computation in hash tables
  9. tflag tflag // extra type information flags
  10. align uint8 // alignment of variable with this type
  11. fieldalign uint8 // alignment of struct field with this type
  12. kind uint8 // enumeration for C
  13. alg *typeAlg // algorithm table
  14. gcdata *byte // garbage collection data
  15. str nameOff // string form
  16. ptrToThis typeOff // type for pointer to this type, may be zero
  17. }

2.iface

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

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

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

  1. struct Iface
  2. {
  3. Itab* tab;
  4. void* data;
  5. };
  6. struct Itab
  7. {
  8. InterfaceType* inter;
  9. Type* type;
  10. void (*fun[])(void);
  11. };

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

再来看下nil的定义。

nil的定义

  1. // nil is a predeclared identifier representing the zero value for a pointer, channel, func, interface, map, or slice type.
  2. 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。

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

例如,

  1. var w io.Writer

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

例如,下面的例子。

  1. package main
  2. import ("fmt")
  3. func main(){
  4. var a interface{} = nil // tab = nil, data = nil
  5. var b interface{} = (*int)(nil) // tab 包含 *int 类型信息, data = nil
  6. fmt.Println(a==nil)
  7. fmt.Println(b==nil)
  8. }

output:

true

false

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

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

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

可以借助反射来判断。

  1. func IsNil(i interface{}) bool {
  2. defer func() {
  3. recover()
  4. }()
  5. vi := reflect.ValueOf(i)
  6. return vi.IsNil()
  7. }

其中,IsNil定义如下:

  1. func (v Value) IsNil() bool

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

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

或者修改成这样:

  1. func IsNil(i interface{}) bool {
  2. vi := reflect.ValueOf(i)
  3. if vi.Kind() == reflect.Ptr {
  4. return vi.IsNil()
  5. }
  6. return false
  7. }

总结

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

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

参考

https://golang.org/src/builtin/builtin.go?h=var+nil+Type#L101

《Go学习笔记--雨痕》

http://legendtkl.com/2017/07/01/golang-interface-implement/

https://www.jianshu.com/p/97bfe8104e03

golang interface判断为空nil的更多相关文章

  1. go中interface空指针不为nil判断方法

    interface空指针不为nil 当把一个空指针对象赋值给一个interface后,再判断!= nil就不再成立了 代码如下 package main import "fmt" ...

  2. Golang Interface 解析

    转自 https://zhuanlan.zhihu.com/p/27652856 先看一段代码: 123456789101112 func (x interface{}) { if x == nil ...

  3. sql server case when 判断为空

    代码如下 select distinct G.* ,(select BUSINESS_NAME from BusinessInfo where BusinessInfo.BUSINESS_BID=G. ...

  4. javascript判断非空

    /* *判断非空 * */ function isEmpty(val){ if(val == null)return true; if(val == undefined || val == 'unde ...

  5. js判断为空Null与字符串为空简写方法

    下面就是有关判断为空的简写方法.   代码如下: if (variable1 !== null || variable1 !== undefined || variable1 !== '') {  v ...

  6. m_Orchestrate learning system---三十二、数据库字段判断为空时容易出现问题,如何从根本上解决这个问题

    m_Orchestrate learning system---三十二.数据库字段判断为空时容易出现问题,如何从根本上解决这个问题 一.总结 一句话总结:字段禁止为空,设置默认值0即可 禁止 空 默认 ...

  7. Golang并行判断素数

    ## Golang多核判断素数方式 package main import ( "bufio" "fmt" "os" "runti ...

  8. mybatis传入0但判断为空

    当是数字类型时,传入0会判断为空,当值为空,显示在页面上的将会是0:

  9. Java中判断非空对象.

    Java中经常会遇到判断非空的时候. 有的时候判断了非空但是还是报空指针,为什么.? 判断的时候一般都会判断两次.类似于: Org o = new Org(); if ( o.getId()!=nul ...

随机推荐

  1. 2019-03-07-day006-小数据池

    01 昨日内容回顾 字典: 映射,{} 键值对的形式存储,容器型数据类型,key 唯一的,可哈希的,value任意数据类型,对象. 3.6之前无序的, 3.6之后,有序的(第一次创建字典的顺序) 特点 ...

  2. JFrame,JPanel,JLabel详解

    JFrame是一个顶层的框架类,好比一个窗户的框子.也是一个容器类.这个框子可以嵌入几个玻璃窗. JPanel是一个容器类,相当于一大玻璃窗. JLabel等是一些基础组件,它必须置于某个容器里,类似 ...

  3. Smali插桩打日志

    一.smali目录下新建crack.smali,内容如下: .class public Lcrack; .super Ljava/lang/Object; .source "crack.ja ...

  4. 构建NDK交叉编译链

    有时我们需要单独编译个c文件,生成一个ELF在Android上面跑,NDK提供了一个 make-standalone-toolchain.sh 脚本,用于生成一套特定平台的交叉编译工具链 使用方法如下 ...

  5. Django之模型层-了解ORM

    ORM(对象-关系-映射)简单使用 ORM实现了数据模型与数据库的解耦合,即数据模型的设计不需要指定特定的数据库,通过python代码可以直接对数据库实现增删改查 MySQL语法 #sql中的表 #创 ...

  6. 【c++基础】从json文件提取数据

    前言 标注数据导出文件是json格式的,也就是python的dict格式,需要读取标注结果,可以使用c++或者python,本文使用c++实现的. JsonCpp简介 JsonCpp是一种轻量级的数据 ...

  7. 特征选择之Chi卡方检验

    特征选择之Chi卡方检验 卡方值越大,说明对原假设的偏离越大,选择的过程也变成了为每个词计算它与类别Ci的卡方值,从大到小排个序(此时开方值越大越相关),取前k个就可以. 针对英文纯文本的实验结果表明 ...

  8. [LeetCode&Python] Problem 1: Two Sum

    Problem Description: Given an array of integers, return indices of the two numbers such that they ad ...

  9. react状态提升问题::::

    父组件传值给子组件,只需要在组件上写上naverightstates={this.state.naverightstates},然后在子组件里面引用this.props.naverightstates ...

  10. ccf-170904-通信网络

    ccf-170904-通信网络 题目分析: 有向图 如果a可以直接或者间接连接b则a与b相互知晓 一共有多少个点知道n个点 刚开始算错复杂度,优化后反而超时 ...事实无脑爆搜这道题也是可取的因为只有 ...