
Hi,大家好,我是明哥,
在自己學習 Golang 的這段時間里,我寫了詳細的學習筆記放在我的個人微信公眾號 《Go編程時光》,對于 Go 語言,我也算是個初學者,因此寫的東西應該會比較適合剛接觸的同學,如果你也是剛學習 Go 語言,不防關注一下,一起學習,一起成長,
我的在線博客:http://golang.iswbm.com
我的 Github:github.com/iswbm/GolangCodingTime
1. 靜態型別
所謂的靜態型別(即 static type),就是變數宣告的時候的型別,
var age int // int 是靜態型別
var name string // string 也是靜態型別
它是你在編碼時,肉眼可見的型別,
2. 動態型別
所謂的 動態型別(即 concrete type,也叫具體型別)是 程式運行時系統才能看見的型別,
這是什么意思呢?
我們都知道 空介面 可以承接什么問題型別的值,什么 int 呀,string 呀,都可以接收,
比如下面這幾行代碼
var i interface{}
i = 18
i = "Go編程時光"
第一行:我們在給 i 宣告了 interface{} 型別,所以 i 的靜態型別就是 interface{}
第二行:當我們給變數 i 賦一個 int 型別的值時,它的靜態型別還是 interface{},這是不會變的,但是它的動態型別此時變成了 int 型別,
第三行:當我們給變數 i 賦一個 string 型別的值時,它的靜態型別還是 interface{},它還是不會變,但是它的動態型別此時又變成了 string 型別,
從以上,可以知道,不管是 i=18 ,還是 i="Go編程時光",都是當程式運行到這里時,變數的型別,才發生了改變,這就是我們最開始所說的 動態型別是程式運行時系統才能看見的型別,
3. 介面組成
每個介面變數,實際上都是由一 pair 對(type 和 data)組合而成,pair 對中記錄著實際變數的值和型別,
比如下面這條陳述句
var age int = 25
我們宣告了一個 int 型別變數,變數名叫 age ,其值為 25

知道了介面的組成后,我們在定義一個變數時,除了使用常規的方法(可參考:02. 學習五種變數創建的方法)
也可以使用像下面這樣的方式
package main
import "fmt"
func main() {
age := (int)(25)
//或者使用 age := (interface{})(25)
fmt.Printf("type: %T, data: %v ", age, age)
}
輸出如下
type: int, data: 25
4. 介面細分
根據介面是否包含方法,可以將介面分為 iface 和 eface,
iface
第一種:iface,表示帶有一組方法的介面,
比如
type Phone interface {
call()
}
iface 的具體結構可用如下一張圖來表示

iface 的原始碼如下:
// runtime/runtime2.go
// 非空介面
type iface struct {
tab *itab
data unsafe.Pointer
}
// 非空介面的型別資訊
type itab struct {
inter *interfacetype // 介面定義的型別資訊
_type *_type // 介面實際指向值的型別資訊
link *itab
bad int32
inhash int32
fun [1]uintptr // 介面方法實作串列,即函式地址串列,按字典序排序
}
// runtime/type.go
// 非空介面型別,介面定義,包路徑等,
type interfacetype struct {
typ _type
pkgpath name
mhdr []imethod // 介面方法宣告串列,按字典序排序
}
// 介面的方法宣告
type imethod struct {
name nameOff // 方法名
ityp typeOff // 描述方法引數回傳值等細節
}
eface
第二種:eface,表示不帶有方法的介面
比如
var i interface{}
eface 的原始碼如下:
// src/runtime/runtime2.go
// 空介面
type eface struct {
_type *_type
data unsafe.Pointer
}

5.理解動態型別
前兩節,我們知道了什么是動態型別?如何讓一個物件具有動態型別?
后兩節,我們知道了介面分兩種,它們的內部結構各是什么樣的?
那最后一節,可以將前面四節的內容結合起來,看看在給一個空介面型別的變數賦值時,介面的內部結構會發生怎樣的變化 ,
iface
先來看看 iface,有如下一段代碼:
var reader io.Reader
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
return nil, err
}
reader = tty
第一行代碼:var reader io.Reader ,由于 io.Reader 介面包含 Read 方法,所以 io.Reader 是 iface,此時 reader 物件的靜態型別是 io.Reader,暫無動態型別,

最后一行代碼:reader = tty,tty 是一個 *os.File 型別的實體,此時reader 物件的靜態型別還是 io.Reader,而動態型別變成了 *os.File,

eface
再來看看 eface,有如下一段代碼:
//不帶函式的interface
var empty interface{}
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
return nil, err
}
empty = tty
第一行代碼:var empty interface{},由于 interface{} 是一個 eface,其只有一個 _type 可以存放變數型別,此時 empty 物件的(靜態)型別是 nil,

最后一行代碼:empty = tty,tty 是一個 *os.File 型別的實體,此時 _type 變成了 *os.File,

6. 反射的必要性
由于動態型別的存在,在一個函式中接收的引數的型別有可能無法預先知曉,此時我們就要對引數進行反射,然后根據不同的型別做不同的處理,
關于 反射 的內容有點多,我將其安排在下一篇,
參考文章
- 圖解go反射實作原理

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/5024.html
標籤:Go
上一篇:Go 其一 基礎內容
