当前位置: 首页 > 科技观察

Go面试题:Go接口的一个“坑”及其原理分析

时间:2023-03-14 11:58:33 科技观察

本文转载自微信公众号《我的大脑是炸鱼》,作者陈建宇。转载本文请联系脑筋急转弯公众号。大家好,我是炸鱼。前几天在读者交流群看到一个朋友对接口的使用有很多疑惑。无独有偶,在网上也看到围棋面试的时候有朋友被问到:今天特地分享网上博客的截图,让大家避免这个坑。例1第一个例子,代码如下:funcmain(){varvinterface{}v=(*int)(nil)fmt.Println(v==nil)}你认为输出结果是什么?答案是:假的为什么不真实。显然,它们已被强制设置为零。Go编译器有问题吗?例2第二个例子,代码如下:funcmain(){vardata*bytevarininterface{}fmt.Println(data,data==nil)fmt.Println(in,in==nil)in=datafmt.Println(in,in==nil)}你认为输出是什么?答案是:truetruefalse这个就更奇怪了,为什么data和in变量确实是输出结果是nil,判断结果也是true。变量数据一赋值给变量in,世界怎么就变了呢?输出结果还是nil,但是判断变成false。令人惊讶的是,结果与上面的第一个示例相似。接口判断和想象的不一样的根本原因是接口不是指针类型,虽然看起来很像,误导了很多人。再深入到接口,接口中有两种数据结构:runtime.eface结构:表示一个空接口,不包含任何方法,也称为空接口。runtime.iface结构:表示包含方法的接口。查看两者对应的底层数据结构:typeefacestruct{_type*_typedataunsafe.Pointer}typeifacestruct{tab*itabdataunsafe.Pointer},你会发现interface不是简单的值,而是分为类型和值。所以按照传统的认知,这个nil不是另一个nil。只有类型和值同时为nil时,接口的nil判断才会为真。解决方案与其说是一种解决方案,不如说是一种巧妙的打破局面的方法。在不改变类型的情况下,其中一种方法是使用反射,如下代码所示::=reflect.ValueOf(i)ifvi.Kind()==reflect.Ptr{returnvi.I??sNil()}returnfalse}使用反射判断nil的值,在反射中会有接口类型的特殊处理,最后是输出结果为:true,效果达到。对于其他的方法,就是改变原来的程序逻辑,比如:对value做一个nil判断,然后返回到界面设置。返回具体值类型而不是接口。总结Go接口是Go语言中最常用的类型之一。如果你习惯了iferr!=nil,很容易踩进去。