Golang中匿名結構體和匿名字段,結構體嵌套,模拟繼承性學習
- 一.匿名結構體和匿名字段
-
- 1.匿名結構體
- 2.匿名字段
- 二.結構體嵌套
-
- 1.執行個體
- 三.結構體模拟繼承性
-
- 1.執行個體
- 2.結構體嵌套`is a`和`has a`差別
這篇文章也是結構體的學習,不過,如果沒有結構體
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)
是一種
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
差別
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 // 聚合關系
}
文法上的差別就是:嵌套的結構體是否匿名
結構體的記錄暫時記錄到這裡,如果後續有新的知識,會持續更新。