這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
在golang
中,介面值是由兩部分組成的,一部分是介面的類型,另一部分是該類型對應的值,我們稱其為動態類型和動態值。
這個概念該如何理解呢?我們先看一段代碼:
var w io.Writer // type-<nil>w = new(bytes.Buffer) // type-*bytes.Bufferw = nil // type-<nil>
這裡先定義一個變數w
,然後再為其賦值,可以看到,變數w
的type都是不太一樣的,可以用fmt
的%T
來查看其動態類型。
fmt.Printf("%T\n",w)
在第一行定義變數w的時候,聲明了其類型為io.Writer
,這裡是真正意義上的空介面,為什麼是空介面,就是它的類型和值都為nil
,在這裡可以用==
或者!=
來和nil
做判斷。
w == nil // return true
在第二行為變數w
賦值的時候,此時w
的動態類型為*bytes.Buffer
,然後動態值是一個指向新分配的緩衝區的指標。
package bytestype Buffer struct { buf []byte // contents are the bytes buf[off : len(buf)] off int // read at &buf[off], write at &buf[len(buf)] bootstrap [64]byte // memory to hold first slice; helps small buffers avoid allocation. lastRead readOp // last read operation, so that Unread* can work correctly.}
此時就可以調用Writer
介面中的方法:
w.Write([]byte("ok"))
順便提一下,如果用第一行中的變數w
來調用Write
方法的話,程式會報錯,調用一個空介面值上的任意方法都會產生Panic
。
第三行為w
賦值的效果就和最初是一樣的了,動態類型和動態值都是nil
,為一個空介面。
說到這裡,大概能明白介面值的意義了,不過還有一個問題,那就是一個介面為空白和一個介面包含null 指標是否是一回事?我們來看一段代碼:
func test(w io.Writer) { if w != nil{ w.Write([]byte("ok")) }}func main() { var buf *bytes.Buffer test(buf)}
如果執行這段程式,回報錯,我們來稍微分析一下,在main()
中,我們首先聲明了一個buf
變數,類型是*bytes.Buffer
指標類型,在調用函數test()
的時候,參數w
會被賦值為動態類型為*bytes.Buffer
,動態值為nil
,也就是w
是一個包含了null 指標值的非空介面。那麼在w != nil
判斷時,這個等式便是成立的,不過這裡也從側面反映出一個現象,就是這種傳參都是值拷貝,那麼看到這裡,這段代碼也應該比較好修改了:
var buf io.Writer // buf = new(bytes.Buffer)
我們再來看一段代碼:
type Test interface {}type Test1 interface { TestFunc()}type Structure struct { a int}func (s *Structure) TestFunc(){ fmt.Println("Ok, Let's rock and roll!")}func fTest(t Test) { fmt.Println(t == nil)}func fTest1(t1 Test1){ fmt.Println(t1 == nil)}func fStructure(s *Structure){ fmt.Println(s == nil)}func main() { var s *Structure = nil fTest(s) // false fTest1(s) // false fStructure(s) // true s.TestFunc() // Ok, Let's rock and roll!}
執行一下代碼,是否和預期的結果一樣呢?Ok, Let's rock and roll!