鍍金池/ 教程/ GO/ 10.5 匿名字段和內(nèi)嵌結(jié)構(gòu)體
4.7 strings 和 strconv 包
13.6 啟動(dòng)外部命令和程序
?# 11.4 類型判斷:type-switch
12.1 讀取用戶的輸入
10.6 方法
12.2 文件讀寫
13 錯(cuò)誤處理與測(cè)試
9.3 鎖和 sync 包
12.3 文件拷貝
?# 11.7 第一個(gè)例子:使用 Sorter 接口排序
?# 11.5 測(cè)試一個(gè)值是否實(shí)現(xiàn)了某個(gè)接口
6.4 defer 和追蹤
12.10 XML 數(shù)據(jù)格式
13.10 性能調(diào)試:分析并優(yōu)化 Go 程序
?# 11.1 接口是什么
2.2 Go 環(huán)境變量
2.6 安裝目錄清單
2.5 在 Windows 上安裝 Go
11.11 Printf 和反射
1.2 語言的主要特性與發(fā)展的環(huán)境和影響因素
9.0 包(package)
7.4 切片重組(reslice)
13.2 運(yùn)行時(shí)異常和 panic
10.2 使用工廠方法創(chuàng)建結(jié)構(gòu)體實(shí)例
12.8 使用接口的實(shí)際例子:fmt.Fprintf
2.4 在 Mac OS X 上安裝 Go
3.8 Go 性能說明
7.2 切片
8.0 Map
3.1 Go 開發(fā)環(huán)境的基本要求
5.6 標(biāo)簽與 goto
6.10 使用閉包調(diào)試
9.5 自定義包和可見性
4.3 常量
?# 11.2 接口嵌套接口
6.5 內(nèi)置函數(shù)
前言
10.8 垃圾回收和 SetFinalizer
2.8 Go 解釋器
13.7 Go 中的單元測(cè)試和基準(zhǔn)測(cè)試
6.8 閉包
4.9 指針
13.1 錯(cuò)誤處理
10.1 結(jié)構(gòu)體定義
5.1 if-else 結(jié)構(gòu)
6.6 遞歸函數(shù)
9.9 通過 Git 打包和安裝
2.7 Go 運(yùn)行時(shí)(runtime)
10.7 類型的 String() 方法和格式化描述符
3.7 其它工具
9.6 為自定義包使用 godoc
11.12 接口與動(dòng)態(tài)類型
13.3 從 panic 中恢復(fù)(Recover)
10.3 使用自定義包中的結(jié)構(gòu)體
11.14 結(jié)構(gòu)體、集合和高階函數(shù)
3.6 生成代碼文檔
9.2 regexp 包
4.1 文件名、關(guān)鍵字與標(biāo)識(shí)符
?# 11.6 使用方法集與接口
7.0 數(shù)組與切片
7.1 聲明和初始化
12.11 用 Gob 傳輸數(shù)據(jù)
5.5 Break 與 continue
1.1 起源與發(fā)展
?# 11 接口(Interfaces)與反射(reflection)
6.9 應(yīng)用閉包:將函數(shù)作為返回值
4.2 Go 程序的基本結(jié)構(gòu)和要素
8.6 將 map 的鍵值對(duì)調(diào)
6.11 計(jì)算函數(shù)執(zhí)行時(shí)間
5.0 控制結(jié)構(gòu)
10.5 匿名字段和內(nèi)嵌結(jié)構(gòu)體
4.6 字符串
3.0 編輯器、集成開發(fā)環(huán)境與其它工具
13.8 測(cè)試的具體例子
7.6 字符串、數(shù)組和切片的應(yīng)用
8.4 map 類型的切片
3.9 與其它語言進(jìn)行交互
7.3 For-range 結(jié)構(gòu)
9.7 使用 go install 安裝自定義包
6.0 函數(shù)
9.8 自定義包的目錄結(jié)構(gòu)、go install 和 go test
6.3 傳遞變長參數(shù)
13.9 用(測(cè)試數(shù)據(jù))表驅(qū)動(dòng)測(cè)試
11.9 空接口
8.1 聲明、初始化和 make
6.2 函數(shù)參數(shù)與返回值
9.11 在 Go 程序中使用外部庫
3.3 調(diào)試器
4.5 基本類型和運(yùn)算符
?# 11.8 第二個(gè)例子:讀和寫
12.5 用 buffer 讀取文件
總結(jié):Go 中的面向?qū)ο?/span>
11.10 反射包
12.7 用 defer 關(guān)閉文件
9.4 精密計(jì)算和 big 包
4.4 變量
6.1 介紹
13.4 自定義包中的錯(cuò)誤處理和 panicking
12.4 從命令行讀取參數(shù)
9.10 Go 的外部包和項(xiàng)目
8.3 for-range 的配套用法
3.5 格式化代碼
10.4 帶標(biāo)簽的結(jié)構(gòu)體
7.5 切片的復(fù)制與追加
?# 11.3 類型斷言:如何檢測(cè)和轉(zhuǎn)換接口變量的類型
5.4 for 結(jié)構(gòu)
4.8 時(shí)間和日期
2.3 在 Linux 上安裝 Go
12 讀寫數(shù)據(jù)
6.12 通過內(nèi)存緩存來提升性能
9.1 標(biāo)準(zhǔn)庫概述
12.6 用切片讀寫文件
10 結(jié)構(gòu)(struct)與方法(method)
8.5 map 的排序
12.9 JSON 數(shù)據(jù)格式
13.5 一種用閉包處理錯(cuò)誤的模式
3.2 編輯器和集成開發(fā)環(huán)境
12.12 Go 中的密碼學(xué)
5.2 測(cè)試多返回值函數(shù)的錯(cuò)誤
6.7 將函數(shù)作為參數(shù)
8.2 測(cè)試鍵值對(duì)是否存在及刪除元素
3.4 構(gòu)建并運(yùn)行 Go 程序
2.1 平臺(tái)與架構(gòu)
5.3 switch 結(jié)構(gòu)

10.5 匿名字段和內(nèi)嵌結(jié)構(gòu)體

10.5.1 定義

結(jié)構(gòu)體可以包含一個(gè)或多個(gè) 匿名(或內(nèi)嵌)字段,即這些字段沒有顯式的名字,只有字段的類型是必須的,此時(shí)類型就是字段的名字。匿名字段本身可以是一個(gè)結(jié)構(gòu)體類型,即 結(jié)構(gòu)體可以包含內(nèi)嵌結(jié)構(gòu)體

可以粗略地將這個(gè)和面向?qū)ο笳Z言中的繼承概念相比較,隨后將會(huì)看到它被用來模擬類似繼承的行為。Go 語言中的繼承是通過內(nèi)嵌或組合來實(shí)現(xiàn)的,所以可以說,在 Go 語言中,相比較于繼承,組合更受青睞。

考慮如下的程序:

示例 10.8 structs_anonymous_fields.go

package main

import "fmt"

type innerS struct {
    in1 int
    in2 int
}

type outerS struct {
    b    int
    c    float32
    int  // anonymous field
    innerS //anonymous field
}

func main() {
    outer := new(outerS)
    outer.b = 6
    outer.c = 7.5
    outer.int = 60
    outer.in1 = 5
    outer.in2 = 10

    fmt.Printf("outer.b is: %d\n", outer.b)
    fmt.Printf("outer.c is: %f\n", outer.c)
    fmt.Printf("outer.int is: %d\n", outer.int)
    fmt.Printf("outer.in1 is: %d\n", outer.in1)
    fmt.Printf("outer.in2 is: %d\n", outer.in2)

    // 使用結(jié)構(gòu)體字面量
    outer2 := outerS{6, 7.5, 60, innerS{5, 10}}
    fmt.Println("outer2 is:", outer2)
}

輸出:

outer.b is: 6
outer.c is: 7.500000
outer.int is: 60
outer.in1 is: 5
outer.in2 is: 10
outer2 is:{6 7.5 60 {5 10}}

通過類型 outer.int 的名字來獲取存儲(chǔ)在匿名字段中的數(shù)據(jù),于是可以得出一個(gè)結(jié)論:在一個(gè)結(jié)構(gòu)體中對(duì)于每一種數(shù)據(jù)類型只能有一個(gè)匿名字段。

10.5.2 內(nèi)嵌結(jié)構(gòu)體

同樣地結(jié)構(gòu)體也是一種數(shù)據(jù)類型,所以它也可以作為一個(gè)匿名字段來使用,如同上面例子中那樣。外層結(jié)構(gòu)體通過 outer.in1 直接進(jìn)入內(nèi)層結(jié)構(gòu)體的字段,內(nèi)嵌結(jié)構(gòu)體甚至可以來自其他包。內(nèi)層結(jié)構(gòu)體被簡單的插入或者內(nèi)嵌進(jìn)外層結(jié)構(gòu)體。這個(gè)簡單的“繼承”機(jī)制提供了一種方式,使得可以從另外一個(gè)或一些類型繼承部分或全部實(shí)現(xiàn)。

另外一個(gè)例子:

示例 10.9 embedd_struct.go

package main

import "fmt"

type A struct {
    ax, ay int
}

type B struct {
    A
    bx, by float32
}

func main() {
    b := B{A{1, 2}, 3.0, 4.0}
    fmt.Println(b.ax, b.ay, b.bx, b.by)
    fmt.Println(b.A)
}

輸出:

1 2 3 4
{1 2}

練習(xí) 10.5 anonymous_struct.go:

創(chuàng)建一個(gè)結(jié)構(gòu)體,它有一個(gè)具名的 float 字段,2 個(gè)匿名字段,類型分別是 int 和 string。通過結(jié)構(gòu)體字面量新建一個(gè)結(jié)構(gòu)體實(shí)例并打印它的內(nèi)容。

10.5.3 命名沖突

當(dāng)兩個(gè)字段擁有相同的名字(可能是繼承來的名字)時(shí)該怎么辦呢?

  1. 外層名字會(huì)覆蓋內(nèi)層名字(但是兩者的內(nèi)存空間都保留),這提供了一種重載字段或方法的方式;
  2. 如果相同的名字在同一級(jí)別出現(xiàn)了兩次,如果這個(gè)名字被程序使用了,將會(huì)引發(fā)一個(gè)錯(cuò)誤(不使用沒關(guān)系)。沒有辦法來解決這種問題引起的二義性,必須由程序員自己修正。

例子:

type A struct {a int}
type B struct {a, b int}

type C struct {A; B}
var c C

規(guī)則 2:使用 c.a 是錯(cuò)誤的,到底是 c.A.a 還是 c.B.a 呢?會(huì)導(dǎo)致編譯器錯(cuò)誤:ambiguous DOT reference c.a disambiguate with either c.A.a or c.B.a。

type D struct {B; b float32}
var d D

規(guī)則1:使用 d.b 是沒問題的:它是 float32,而不是 Bb。如果想要內(nèi)層的 b 可以通過 d.B.b 得到。

鏈接