Go 面試題:Go interface 的一個 “坑” 及原理分析
大家好,我是煎魚。
前幾天在讀者交流羣裏看到一位小夥伴,針對 interface 的使用有了比較大的疑惑。
無獨有偶,我也在網上看到有小夥伴在 Go 面試的時候被問到了:
來自網上博客的截圖
今天特意分享出來讓大家避開這個坑。
例子一
第一個例子,如下代碼:
func main() {
var v interface{}
v = (*int)(nil)
fmt.Println(v == nil)
}
你覺得輸出結果是什麼呢?
答案是:
false
爲什麼不是 true
。明明都已經強行置爲 nil
了。是不是 Go 編譯器有問題?
例子二
第二個例子,如下代碼:
func main() {
var data *byte
var in interface{}
fmt.Println(data, data == nil)
fmt.Println(in, in == nil)
in = data
fmt.Println(in, in == nil)
}
你覺得輸出結果是什麼呢?
答案是:
<nil> true
<nil> true
<nil> false
這可就更奇怪了,爲什麼剛剛聲明出來的 data
和 in
變量,確實是輸出結果是 nil
,判斷結果也是 true
。
怎麼把變量 data
一賦予給變量 in
,世界就變了?輸出結果依然是 nil
,但判定卻變成了 false
。
和上面的第一個例子結果類似,真是神奇。
原因
interface 判斷與想象中不一樣的根本原因是,interface 並不是一個指針類型,雖然他看起來很像,以至於誤導了不少人。
我們鑽下去 interface,interface 共有兩類數據結構:
-
runtime.eface
結構體:表示不包含任何方法的空接口,也稱爲 empty interface。 -
runtime.iface
結構體:表示包含方法的接口。
看看這兩者相應的底層數據結構:
type eface struct {
_type *_type
data unsafe.Pointer
}
type iface struct {
tab *itab
data unsafe.Pointer
}
你會發現 interface 不是單純的值,而是分爲類型和值。
所以傳統認知的此 nil 並非彼 nil,必須得類型和值同時都爲 nil 的情況下,interface 的 nil 判斷纔會爲 true。
解決辦法
與其說是解決方法,不如說是委婉的破局之道。在不改變類型的情況下,方法之一是利用反射(reflect),如下代碼:
func main() {
var data *byte
var in interface{}
in = data
fmt.Println(IsNil(in))
}
func IsNil(i interface{}) bool {
vi := reflect.ValueOf(i)
if vi.Kind() == reflect.Ptr {
return vi.IsNil()
}
return false
}
利用反射來做 nil 的值判斷,在反射中會有針對 interface 類型的特殊處理,最終輸出結果是:true,達到效果。
其他方法的話,就是改變原有的程序邏輯,例如:
-
對值進行 nil 判斷,再返回給 interface 設置。
-
返回具體的值類型,而不是返回 interface。
總結
Go interface 是 Go 語言中最常用的類型之一,大家用慣了 if err != nil
就很容易順手就踩進去了。
建議大家要多留個心眼,如果對 interface 想要有更進一步的瞭解,可以看看我的這篇深入解析的文章:《一文喫透 Go 語言解密之接口 interface》。
小夥伴們有沒有踩到過,或遇到過 interface 相關的 “坑” 呢?歡迎大家下方留言討論,分享出來。
大家一起衝!
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/vNACbdSDxC9S0LOAr7ngLQ