GoLang 物件導向

📢 本文由 gemini-3-flash-preview 翻譯

Golang 系列

Hello GoLang: https://blog.yexca.net/archives/154
GoLang (var and const) 變數與常數: https://blog.yexca.net/archives/155
GoLang (func) 函式: https://blog.yexca.net/archives/156
GoLang (slice and map) 切片: https://blog.yexca.net/archives/160
GoLang (OOP) 物件導向: 本文
GoLang (reflect) 反射: https://blog.yexca.net/archives/204
GoLang (struct tag) 結構體標籤: https://blog.yexca.net/archives/205
GoLang (goroutine) Go 協程: https://blog.yexca.net/archives/206
GoLang (channel) 通道: https://blog.yexca.net/archives/207


透過使用結構體構建類別與物件的概念

了解物件導向: 物件導向基礎

struct

首先是自訂型別,使用 type 關鍵字,類似於 C

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import "fmt"

type myType int

func main() {
    var a myType
    fmt.Println("a =", a)
    fmt.Printf("type of a is %T", a)
}

/*
 * 輸出
 * a = 0
 * type of a is main.myTye
 */

定義型別使用 struct

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import "fmt"

type Person struct {
    name string
    age  int
}

fun     main() {
    var zhang Person
    zhang.name = "zhangSan"
    zhang.age = 20

    fmt.Print(zhang)
}

函式傳遞相關,不使用指標是傳值 (Value Passing)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import "fmt"

type Person struct {
    name string
    age  int
}

// 傳值,不會修改原始資料
func changeName(person Person) {
    person.name = "liSi"
}

// 傳址,會修改原始資料
func changeAge(person *Person) {
    person.age = 18
}

func main() {
    var zhang Person
    zhang.name = "zhangSan"
    zhang.age = 20

    fmt.Println(zhang)

    changeName(zhang)
    fmt.Println(zhang)

    changeAge(&zhang)
    fmt.Println(zhang)
}

封裝

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import "fmt"

// 類別
type Person struct {
    // 屬性
    name string
    age  int
}

// 類別的方法
func (this *Person) SetName(name string) {
    this.name = name
}
func (this *Person) SetAge(age int) {
    this.age = age
}
func (this *Person) GetName() string {
    return this.name
}
func (this *Person) GetAge() int {
    return this.age
}

func main() {
    person := Person{name: "zhangSan", age: 18}
    fmt.Println(person)
}

注意到上述類別名稱、方法名稱首字母為大寫,代表該類別、方法可以被其他套件存取 (java public),若首字母小寫則只能由此套件存取 (java private),屬性名稱也是同理

繼承

以下程式碼處於同一檔案,父類別

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 父類別
type Person struct {
    name string
    age  int
}

// 父類別的方法
func (this *Person) Eat() {
    fmt.Println("Person Eat...")
}
func (this *Person) Walk() {
    fmt.Println("Person Walk...")
}

子類別

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 子類別
type SuperMan struct {
    Person     // 繼承父類別
    level  int // 子類別屬性
}

// 子類別覆寫父類別方法
func (this *SuperMan) Walk() {
    fmt.Println("SuperMan Walk")
}
// 子類別特有方法
func (this *SuperMan) Fly() {}
    

main 函式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func main() {
    // 定義子類別屬性,方式一
    superMan1 := SuperMan{Person{"zhangSan", 18}, 4}
    // 定義子類別屬性,方式二
    var superMan2 SuperMan
    superMan2.name = "liSi" // 因為同一個套件,可以存取父類別屬性
    superMan2.age = 20
    superMan2.level = 5

    // 子類別呼叫父類別方法
    superMan1.Eat()
    // 子類別覆寫方法
    superMan1.Walk()
    // 子類別方法
    superMan2.Fly()
}

多型

以下程式碼處於同一檔案,定義介面

1
2
3
4
5
6
// 介面,本質是一個指標
type Animal interface {
    Sleep()
    GetName() string
    GetType() string
}

實作類別一

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 實作類別一
type Cat struct {
    name string
    kind string
}

// 實作介面所有方法
func (this *Cat) Sleep() {
    fmt.Println("Cat Sleep()")
}
func (this *Cat) GetName() string {
    return this.name
}
func (this *Cat) GetType() string {
    return this.kind
}

實作類別二

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 實作類別二
type Dog struct {
    name string
    kind string
}

// 實作介面所有方法
func (this *Dog) Sleep() {
    fmt.Println("Cat Sleep()")
}
func (this *Dog) GetName() string {
    return this.name
}
func (this *Dog) GetType() string {
    return this.kind
}

func ShowAnimal(animal Animal) {
    fmt.Println(animal)
}

main

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
func ShowAnimal(animal Animal) {
    fmt.Println(animal)
}

func main() {
    var animal Animal
    animal = &Cat{"cat1", "cat"}
    animal.Sleep()
    animal = &Dog{"dog1", "dog"}
    animal.Sleep()

    cat := Cat{"cat2", "cat"}
    dog := Dog{"dog2", "dog"}

    ShowAnimal(&cat)
    ShowAnimal(&dog)

}

萬用型別與斷言

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import "fmt"

// 使用空介面表示萬用資料型別
func f(arg interface{}) {
    // 型別斷言機制(即型別轉換)
    value, flag := arg.(string)
    if !flag {
        fmt.Println("arg is not string type")
    } else {
        fmt.Println("arg is string type, arg =", value)
    }
}

func main() {
    // 可以傳入任意資料型別,包括自訂
    f("abc")
    f(123)
    f(3.14)
}

/*
 * 輸出
 * arg is string type, arg = abc
 * arg is not string type
 * arg is not string type
 */