天天看點

Golang學習——結構體struct(二)一.匿名結構體和匿名字段二.結構體嵌套三.結構體模拟繼承性

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

差別

總結一下:

Golang結構體嵌套,會衍生出兩種關系:

1.模拟繼承性 - >

is a

,如下:

type A struct{
	field
}

type B struct{
	A  // 匿名字段
}
           

2.模拟聚合性 - >

has a

,如下:

type C struct{
	field
}
type D struct{
	c C  // 聚合關系
} 
           

文法上的差別就是:嵌套的結構體是否匿名

結構體的記錄暫時記錄到這裡,如果後續有新的知識,會持續更新。

繼續閱讀