Go 語言極速入門4 - 物件導向

weixin_34321977發表於2018-11-11

一、自定義類

定義類:type 類名 struct

1.1、定義類

// user 類
type user struct {
    name       string
    email      string
    ext        int
    privileged bool
}

// admin 類
type admin struct {
    // 自定義類
    person user
    // 內建型別
    level string
}

1.2、例項化類

    // 1. 建立 user 變數,所有屬性初始化為其零值
    var bill user
    fmt.Println(bill) // {  0 false}

    // 2. 建立 user 變數,並初始化屬性值
    lisa := user{
        name:       "nana",
        email:      "117@qq.com",
        ext:        123,
        privileged: true,
    }
    fmt.Println(lisa) // {nana 117@qq.com 123 true}
    // 直接使用屬性值,屬性值的順序要與 struct 中定義的一致
    lisa2 := user{"nana", "117@qq.com", 123, true}
    fmt.Println(lisa2) // {nana 117@qq.com 123 true}

    // 3. 含有自定義型別的 struct 進行初始化
    fred := admin{
        person: user{
            name:       "nana",
            email:      "117@qq.com",
            ext:        123,
            privileged: true,
        },
        level: "super",
    }
    fmt.Println("fred:", fred) // fred: {{nana 117@qq.com 123 true} super}

二、方法

  • 方法的定義方法實際上也是函式,只是在宣告時,在關鍵字 func 和方法名之間增加了一個引數
  • 普通的函式定義 func 方法名(入參) 返回值
  • 自定義型別的方法定義 func (接收者) 方法名(入參) 返回值
  • 方法的值傳遞和指標傳遞
  • func (u user) notify() 拷貝一份 user
  • func (u *user) changeEmail(newEmail string) 傳遞指標(即地址),內部改變會影響外部
type user struct {
    name  string
    email string
}

// 普通的函式定義 "func 方法名(入參) 返回值"
// 自定義型別的函式定義 "func (接收者) 方法名(入參) 返回值"
// 值傳遞,拷貝一份 user
func (u user) notify() {
    fmt.Println("pass-by-value", u.name, u.email)
    u.email = "0@qq.com"
}

// 傳遞指標(即地址),內部改變會影響外部
func (u *user) changeEmail(newEmail string) {
    // 不需要 (*u).email
    u.email = newEmail
}

func main() {
    // 1. user型別的值可以用來呼叫使用值接收者宣告的方法
    bill := user{"bill", "1@qq.com"}
    bill.notify() // {"bill", "1@qq.com"}
    fmt.Println("1", bill.email) // "1@qq.com"

    // 2. 指向 user 型別值的指標也可以用來呼叫使用值接收者宣告的方法
    lisa := &user{"lisa", "2@qq.com"}
    // 等價於 (*lisa).notify()
    // 注意:把 lisa 指標指向的 user 物件複製了一份,"再強調一次,notify 操作的是一個副本,只不過這次操作的是從 lisa 指標指向的值的副本。"
    lisa.notify() // {"lisa", "2@qq.com"}
    fmt.Println("2", lisa.email) // "0@qq.com"(錯)  2@qq.com(對)

    // 3.user 型別的值可以用來呼叫使用指標接收者宣告的方法
    // 等價於 (&bill).changeEmail ("100@qq.com"),注意 changeEmail 接收的是一個指標
    bill.changeEmail("100@qq.com")
    fmt.Println("3", bill.email) // "100@qq.com"

    // 4.指向 user 型別值的指標可以用來呼叫使用指標接收者宣告的方法
    lisa.changeEmail("200@qq.com")
    fmt.Println("4", lisa.email) // "200@qq.com"
}

注意:著重注意2,“再強調一次,notify 操作的是一個副本,只不過這次操作的是從 lisa 指標指向的值的副本。”

三、嵌入型別

  • Go 語言允許使用者擴充套件或者修改已有型別的行為。這個功能是通過嵌入型別 type embedding 完成的。嵌入型別是將已有的型別直接宣告在新的結構型別裡。被嵌入的型別被稱為新的外部型別的內部型別。
  • Java 通常可以通過繼承或組合的方式實現嵌入型別要實現的功能。
  • "要嵌入一個型別,只需要宣告這個型別的名字就可以了",即 user 而不是 u user, u user 是宣告欄位
  • 內部型別的方法可以被提升到外部型別直接使用
type user struct {
    name  string
    email string
}
// 注意該方法是 user 的方法(接收者為 user)
func (u *user) notify() {
    fmt.Println("notify", *u)
}

type admin struct {
    // 嵌入型別: "要嵌入一個型別,只需要宣告這個型別的名字就可以了"
    // 注意:不是 u user, u user 是宣告欄位
    // user 是外部型別 admin 的內部型別
    user
    level string
}

func main() {
    ad := admin{
        user: user{"nana", "110@qq.com"},
        level: "super",
    }
    // 我們可以直接訪問內部型別的方法
    ad.user.notify() // notify {nana 110@qq.com}
    // 內部型別的方法也被提升到外部型別
    ad.notify() // notify {nana 110@qq.com}
}

相關文章