Go語言規格說明書 之 結構體類型(Struct types)

go version go1.11 windows/amd64

 

本文爲閱讀Go語言中文官網的規則說明書(https://golang.google.cn/ref/spec)而做的筆記,介紹Go語言的 結構體類型(Struct types)

 

結構體 在Go語言中很重要,用於組織數據,類似OOP中的類,但是,Go語言的 結構體 中只有數據定義,沒有 OOP中的 類方法、實例方法 等概念。

Go語言的結構體 和 C語言的結構體 有些類似,不過俺忘記了C語言結構體的具體用法了,下面介紹Go語言的結構體。

 

結構體 是 命名元素 的 一個 序列,這些 命名元素 被稱爲 域(Fields),每一個 域 都有一個 名稱 和 類型。

注意,域 的名稱可以 顯示 或 隱式 指定,顯示指定 爲 使用 標識符列表(IdentifierList),隱式指定 爲 嵌入式域(EmbeddedField)。

注意,在結構體中,非空的域名稱 必須是 唯一的(Unique)。

疑問,前面說了 每一個域都有一個名稱和類型,這裏怎麼暗示說有 空域 呢?什麼是空域?域名稱 爲 下劃線(_)的域即使空域!可以存在多個?應該是的。有什麼用處呢?填充 結構體,使其size達到某種長度,還有呢?

下面是官文示例和自己的批註(藍色部分不十分確定):

// An empty struct.
struct {} // 空結構體

// A struct with 6 fields. // 6個域 包括 空域
struct {
	x, y int // 定義了兩個域 x、y,類型都是int,這就是 IdentifierList 了
	u float32
	_ float32  // padding // 空域,下劃線
	A *[]int // 定義一個 名稱爲 A 的 整形數組的指針
	F func() // 定義一個 名稱爲 F 的 函數類型 域——無參數、無返回類型
}

 

什麼是 嵌入式域呢?就是 只給出了 類型但沒有提供域名稱 的域(這也可以!)。嵌入式域 必須(2種情況) 使用類型名T指定,或者 使用指向非接口類型的指針的類型名 *T 指定——此時T本身不能是指針類型(這裏翻譯的有些問題)。

嵌入式域 也是有 域名稱的,它的域名稱就是 其 未限定的類型名稱(unqualified type name)。

下面是官文示例和自己的批註(藍色部分不十分確定):

// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4
struct {
	T1        // field name is T1
	*T2       // field name is T2 // T2不能是接口類型
	P.T3      // field name is T3 // 這個是?不屬於上面的兩種情況吧?屬於情況1?
	*P.T4     // field name is T4 // 屬於情況2?
	x, y int  // field names are x and y
}

官文中嵌入式域的錯誤示例——三個域的域名稱都是 T,衝突了

struct {
	T     // conflicts with embedded field *T and *P.T
	*T    // conflicts with embedded field T and *P.T
	*P.T  // conflicts with embedded field T and *T
}

測試及結果(本想定義一個int、*int的,但這兩者衝突了):

代碼:
type S1 struct {
	int
	float32
}

func main() {
	var sv1 = S1{12, 21.0}
	fmt.Println(sv1)
	fmt.Println(sv1.int, sv1.float32)
}

運行結果:
{12 21}
12 21
嵌入式域測試1

 

注意,在測試中,struct關鍵字前面 多了 type S1,這表示 類型定義——將其後定義的結構體綁定到標識符S1,之後就可以使用S1來定義常量、變量或其它地方使用了。若只是struct,俺目前不曉得怎麼使用,或可能出現在 一次性使用 的場合,未可知也。

 

前面提到了 三種域 了:命名的域、空域、嵌入式域,下面介紹第四種域,還沒看明白,希望寫完本文可以搞清楚。

 

第四種域:一個 結構體 x 的 嵌入式域 的 域 或 方法 f 被稱爲 升級域(Promoted),如果 x.f 是指向 那個 域 或 方法 f(翻譯不準確,請看原文)。

大概能懂,但需要簡單的示例就更明白了!

俺的理解:嵌入式域 是一個 類型嘛,這個類型 可能也是 結構體或什麼的,也會有自己的 域 和 方法,當它作爲 嵌入式域 出現在 另一個結構體中時,它的 域 和 方法就可以 被 另一個結構體直接 調用了。

此時需要注意,嵌入式域 的 域或方法 的 名稱 在 外層 結構體中 需保持 唯一性(Unique)。

升級域 表現的 就像  結構體的 普通域一樣,除了它們不能在 結構體的 組合字面量(請看官文 composite literals) 中作爲域名出現外。

就是說,這些升級域 可以這樣 使用,但是,它們不是結構體真正的域,,如果是這樣的話,還需要 遵守 非空域名 的唯一性原則嗎?

 

官文更進一步解釋了 升級域,下爲翻譯:

給定一個 結構體類型 S、一個 自定義類型(defined type) T,被升級到 結構體S的方法 包含下面兩種情況:

1.S包含 嵌入式域 T,S 和 *S 的 方法集 都會 包含 接收者(receiver)爲T的升級方法,另外,*S 的方法集 還包含 接收者 爲 *T 的升級方法;

2.S包含 嵌入式域 *T,S 和 *S 的 方法集 都會 包含 接收者 爲 T 或 *T 的升級方法;

俺的理解:嵌入式域的所有方法都會被升級,根據 嵌入的方式 不同——類型 或 類型的指針,結構體 或 結構體指針 擁有的 來自 升級方法的 方法集是不同的。

 

好了,結構體的四種域都介紹完了,如果有更多示例就好了,可本文暫時沒有,以後更難說了,下面是最後一部分:域的標籤(tag)。

域在聲明的時候,可以跟一個字符串字面量,這個叫做 標籤(tag),它會成爲對應的域的屬性。

標籤爲 空字符串 和 沒有標籤 一樣。

用法有兩個:反射接口(reflection interface)、結構體類型識別(type identify)——兩個方法請讀者查看官文並摸索,其它情況時均忽略。

在俺看來,tag就是域的註釋,解釋域是幹什麼的。在上面的兩種用法中,可以獲取標籤的信息,而在其它地方就忽略了,或者獲取不到。

官文示例:

struct {
	x, y float64 ""  // an empty tag string is like an absent tag // 空標籤
	name string  "any string is permitted as a tag"
	_    [4]byte "ceci n'est pas un champ de structure" // 法語,Google翻譯:這不是結構領域
}

// A struct corresponding to a TimeStamp protocol buffer.
// The tag strings define the protocol buffer field numbers;
// they follow the convention outlined by the reflect package.
// 使用 reflect 包輸出的 結構體的信息。。要使用反射,那就 從 學習 reflect 包開始吧! struct { microsec uint64 `protobuf:"1"` // 注意,是 反引號 serverIP6 uint64 `protobuf:"2"` // 注意,是 反引號 }

 

好了,結構體類型 就這麼多了,本文就比官文多一個示例,其它的都是翻譯和自己的理解。

其實,結構體 還需要 和 方法定義或聲明 的介紹一起 會更有用,在之前看過的代碼中,存在很多 定義結構體後,再定義其方法——單獨的方法 或者  接口 中的方法的操作。在俺看來,這就是把 面向對象程序設計 中的 數據和操作 拆開了啊(鴨子類型)!這樣更好嗎?還需要更多地體會它的好!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章