Golang中匿名結構體和匿名欄位,結構體巢狀,模擬繼承性學習
這篇文章也是結構體的學習,不過,如果沒有結構體struct
基礎的話,推薦先看Golang學習——結構體struct(一)。
今天主要記錄 匿名結構體和匿名欄位,結構體巢狀,模擬繼承性。
1.匿名結構體
匿名結構體:即沒有名字的結構體,在建立匿名結構體時,同時初始化結構體。
例項:
// 沒有結構體命名過程, 直接建立一個結構體,並初始化
s2 := struct {
name string
age int
}{}
初始化時必須的, 不然會編譯報錯。例項中,我未具體初始化,使用了預設值。通常,這個語法在實際開發中使用較少,作為了解即可
2.匿名欄位
匿名欄位:一個結構體的欄位沒有欄位名
我們定義一個Worker結構體,有兩個匿名欄位,:
type Worker struct {
string //匿名欄位
int
//string 再次定義一個 string 匿名欄位時編譯報錯
}
匿名欄位,預設使用資料型別作為名字,那麼匿名欄位的型別就不能重複,否則會衝突。
通過.
操作正常訪問欄位,如下:
bob := Worker{"Bob", 22}
fmt.Println(bob)
fmt.Println(bob.string)
fmt.Println(bob.int)
輸出:
{Bob 22}
Bob
22
在實際開發中幾乎沒有人會這麼寫的,但是我們仍然有必要了解一下,為模擬繼承性打好基礎。
結構體巢狀:一個結構體可能包含一個欄位,而這個欄位反過來就是一個結構體,這種結構被稱為巢狀結構。
類似Java中的物件的包含關係 has a
1.例項
定義圖書, 學生結構體。其中,學生結構體巢狀了圖書結構體
// 定義 Book 結構體
type Book struct {
bookName string
price float64
}
// 定義 Student 結構體,其中一個欄位是 Book 型別
type Student struct {
name string
age int
book Book // 巢狀 Book 結構體
}
初始化結構體:
// 宣告初始化 book
book1 := Book{
bookName: "三國演義",
price: 56.5,
}
// 宣告初始化 sutdent
student1 := Student{
name: "Tom",
age: 20,
book: book1,
}
fmt.Println("student1 結構體內容:", student1)
fmt.Printf("訪問 book1 中的欄位,書名:%s,價格:%.2f\n", student1.book.bookName, student1.book.price)
輸出:
student1 結構體內容: {Tom 20 {三國演義 56.5}}
訪問 book1 中的欄位,書名:三國演義,價格:56.50
外部結構體物件(student1)訪問內部結構體物件(book1)中的欄位時,不能直接student1.bookName
,這是不允許的, 因為兩個結構體之間的關係是包含關係,二者的欄位是相互獨立的。
那如果就是想 student1.bookName
來獲取圖書名稱呢?這就涉及的結構體的繼承性了。
在學習了前兩小節後,可以順利的學習Golang中的繼承,因為前兩小節是學習繼承的知識鋪墊。
其實Golang並不是純粹的物件導向的程式語言,但也可以實現繼承關係,類似:
Java中的
class_sub extend class_father
- Python中的
class_father(class_sub)
- Python中的
是一種 is a
的關係。
1.例項
Golang中通過巢狀匿名結構體來實現繼承,具體是什麼樣的呢?我們例項操作一下:
//1.定義父類
type Person struct {
name string
age int
}
//2.定義子類
type Student struct {
Person //模擬繼承結構
school string //子類的新增屬性
}
以上程式碼就實現了繼承, 其中 Student
結構體中巢狀了Person
結構體,且Person
必須作為匿名欄位。
例項操作如下:
//1.建立父類的物件
p1 := Person{name: "張三", age: 30}
fmt.Println("父類物件:", p1)
//2.建立子類的物件
s1 := Student{Person{"李四", 17}, "清華大學"}
fmt.Println("子類物件 s1:", s1)
輸出:
父類物件 p1: {張三 30}
子類物件 s1: {{李四 17} 清華大學}
剛剛在 (二)小節中,想通過student1.bookName
來直接訪問書名是不允許,但是在本節學習了繼承關係後,我們可以實現此操作了。
// 子類物件間接訪問父類屬性
s3.Person.name = "王五"
s3.Person.age = 19
s3.school = "清華大學"
fmt.Println("子類物件 s3:", s3)
// 子類物件直接訪問父類屬性
s3.name = "Ruby"
s3.age = 20
fmt.Println("子類物件 s3:", s3)
輸出:
子類物件 s3: {{王五 19} 清華大學}
子類物件 s3: {{Ruby 20} 清華大學}
一般地,在實現了繼承後,開發習慣通常都是直接子類物件.欄位名來獲取屬性值。
但是如果有多重繼承的情況,那就得指定訪問哪個父類的屬性。否則父類之間欄位名重複的話,會引起訪問歧義,編譯會報錯。
我們新增一個Teenager
結構體,其中age
欄位和Person
的age
欄位完成相同,然後Student
也繼承它:
// 新增一個青少年結構體,只有年齡屬性
type Teenager struct {
age int
}
// 學生結構體中多了一個 Teenager 匿名欄位,模擬多重繼承
type Student struct {
Person
Teenager // 新增了一個匿名欄位,是Teenager結構體
school string
}
Teenager
和Person
結構體有個相同的欄位 age
,訪問時需要顯示指定是訪問哪個父類下的age
。否則會編譯報錯。
我們還是使用之前的直接訪問屬性的方式:
// 子類物件直接訪問父類屬性
s3.name = "Ruby"
s3.age = 16 // 重名的欄位會報錯
fmt.Println("子類物件 s3:", s3)
我們試著輸出下報錯資訊:
# command-line-arguments
.\demo09_struct_extend.go:51:4: ambiguous selector s3.age
ambiguous selector s3.age
表明s3.age
有歧義,因為我們Person
和Teenager
有完全相同的欄位。
2.結構體巢狀is a
和has a
區別
總結一下:
Golang結構體巢狀,會衍生出兩種關係:
1.模擬繼承性 - > is a
,如下:
type A struct{
field
}
type B struct{
A // 匿名欄位
}
2.模擬聚合性 - > has a
,如下:
type C struct{
field
}
type D struct{
c C // 聚合關係
}
語法上的區別就是:巢狀的結構體是否匿名
結構體的記錄暫時記錄到這裡,如果後續有新的知識,會持續更新。
本作品採用《CC 協議》,轉載必須註明作者和本文連結