iT邦幫忙

2023 iThome 鐵人賽

DAY 8
0
自我挑戰組

Go in 3o系列 第 8

[Day08] Go in 30 - 比較結構與內嵌結構

  • 分享至 

  • xImage
  •  

一、本篇重點

  • 相互比較結構型別
  • 內嵌結構

二、比較結構型別

如果結構中每一個欄位都相同,且使用可以比較的型別,那麼該結構型別的變數就可以互相比較。

package main

import "fmt"

type point struct {
	x int
	y int
}

func main() {
	point1 := struct {
		x int
		y int
	}{10, 10}

	point2 := struct {
		x int
		y int
	}{10, 10}

	point3 := point{10, 10}

	fmt.Println("point1 == point2:", point1 == point2)
	fmt.Println("point1 == point3:", point1 == point3)
}

Go 語言屬於強行別語言,只有相同的型別才可以做比較,然而結構在這方面比較彈性,如果函式中定義的匿名結構型別和其他結構型別有相同的欄位,Go 允許他們做比較。

package main

import "fmt"

type point struct { //具名函式
	x int
	y int
}

func main() {
	point1 := struct { //匿名函式(有給初始值)
		x int
		y int
	}{10, 10}

	point2 := struct { //匿名函式(有給初始值)
		x int
		y int
	}{10, 10}

	point3 := struct { //匿名函式(沒給初始值)
		x int
		y int
	}{}
	point3.x = 10
	point3.y = 20

	point4 := point{10, 10}

	fmt.Println("point1 == point2:", point1 == point2)
	fmt.Println("point1 == point4:", point1 == point4)
	fmt.Println("point3 == point4:", point3 == point4)
}

輸出結果:

point1 == point2: true
point1 == point4: true
point3 == point4: false

此練習證明,儘管匿名結構變數只能建立一次,但用起來跟具名結構變數一樣,確實可以互相比較。

三、內嵌結構(embedding)

雖然說Go不支援繼承,但其設計者提供了一個有趣替代方式 : 在結構中內嵌其他結構。

內嵌和其他結構型別當成欄位是不一樣的;當我們把結構B嵌入結構A時,B的欄位會被提升(promoted),成結構A自身的欄位,就好像是A的結構型別內定義一樣。

要內嵌一個結構,寫法就像平時定義欄位一樣,只是不必指定欄位名,只須加上來源的結構型名稱即可。
格式如:

type <型別名稱> struct {
    <欄位> <型別> // 正常欄位定義
    <結構型別> // 內嵌結構型別(不寫欄位名稱)
}

我們可以內嵌任何型別,指示內嵌核心型別並非常見做法也沒啥用處,通常會拿來內嵌自訂型別或是結構。

3.1 存取內嵌型別

<結構變數>.<內嵌(結構)型別>

或是存取內嵌結構的欄位

<結構變數>.<內嵌(結構)型別>.<欄位名稱>
<結構變數>.<提升的欄位名稱>

範例1:

package main

import "fmt"

// 定義一個Person結構
type Person struct {
    Name string
    Age int
}

// 定義一個Student結構,內嵌了Person結構
type Student struct {
    Person // 內嵌Person結構,不需要指定欄位名
    Grade int
}

func main() {
// 建立一個Student實例
student := Student{
Person: Person{
Name: "Alice",
Age: 20,
},
Grade: 12,
}

// 存取Student的字段和內嵌的Person結構的字段
fmt.Println("Name:", student.Person.Name) // 存取內嵌結構的字段
fmt.Println("Age:", student.Person.Age) // 存取內嵌結構的字段
fmt.Println("Grade:", student.Grade) // 存取Student結構的字段

// 如果內嵌結構的欄位和Student結構有相同的欄位名,可以使用結構類型的名稱來存取
student.Age = 21 // 存取Student結構的Age字段
fmt.Println("Age (Student):", student.Age)

// 提升的欄位名,不需要使用內嵌結構的名稱,可以直接存取
fmt.Println("Name (提升):", student.Name) // 存取提升的字段
}

範例2:

package main

import "fmt"

// 定義一個Person結構
type Person struct {
    Name string
    Age int
}

// 定義一個Student結構,內嵌了Person結構
type Student struct {
    Person // 內嵌Person結構,不需要指定欄位名
    Grade int
}

func main() {

    // 建立一個Student實例
    student := Student{
        Person: Person{
        Name: "Alice",
        Age: 20,
        },
        Grade: 12,
      }

    fmt.Println("Name:", student.Name)
    fmt.Println("Age:", student.Age)
    fmt.Println("Grade:", student.Grade)
}

然而在真實世界中,嵌入的用法不常見,主要是因為會帶來額外複雜度跟問題,Go 開發者通常偏好直接將其他結構當成目標結構的具名欄位。

補充說明

在Go語言中,字段名的首字母的大小寫有著特定的意義:

如果欄位名稱以大寫字母開頭(例如Age和Name),則該欄位是公開的(exported),可以從套件外部存取。
如果欄位名稱以小寫字母開頭(例如age和name),則該欄位是私有的,只能在套件內部存取。
因此,根據你的需求和設計,你可以選擇將字段名的首字母設為大寫或小寫。

如果你希望Age和Name欄位可以從套件外部訪問,則將它們設為大寫字母開頭是合適的。 如果你希望這些欄位只能在套件內部訪問,則將它們設為小寫字母開頭。

在上面的範例中,我將Age和Name欄位設為大寫字母開頭,因此它們是公開的,可以從套件外部存取。 這樣可以讓你更靈活地控制字段的存取權限。


上一篇
[Day07] Go in 30 - 簡易自訂型別(custom types)、結構(Struct)介紹
下一篇
[Day09] Go in 30 - 替自訂型別(custom types)加上方法(method)
系列文
Go in 3o30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言