Golang 學習——結構體 struct (二)

相守之路發表於2020-05-05

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)

是一種 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欄位和Personage欄位完成相同,然後Student也繼承它:

// 新增一個青少年結構體,只有年齡屬性
type Teenager struct {
    age int
}

// 學生結構體中多了一個 Teenager 匿名欄位,模擬多重繼承
type Student struct {
    Person
    Teenager // 新增了一個匿名欄位,是Teenager結構體
    school string
}

TeenagerPerson結構體有個相同的欄位 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有歧義,因為我們PersonTeenager有完全相同的欄位。

2.結構體巢狀is ahas 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 協議》,轉載必須註明作者和本文連結

相關文章