Go面向物件編程

匿名欄位
- 匿名欄位, 從名字來看, 就是沒有名稱
package main
import "fmt"
type Person struct {
age int
name string
sex bool
}
type MPerson struct {
Person // 匿名欄位
id int
}
func main() {
var mperson MPerson = MPerson{Person{18, "xiaohua", true}, 1}
fmt.Println(mperson)
}
- 匿名欄位, 可以理解成MPerson繼承了Person的欄位
- 匿名欄位的輸出:
fmt.Println(mperson.Person)
- 匿名欄位甚至可以是非結構體資料型別, 輸出的形式同結構體匿名欄位的輸出, 見代碼
package main
import "fmt"
type Person struct {
age int
name string
sex bool
}
type MPerson struct {
Person
int
}
func main() {
var mperson MPerson = MPerson{Person{18, "xiaohua", true}, 1}
fmt.Println(mperson.Person, mperson.int)
}
- 匿名欄位還可以是指標, 指標型別的初始化之前的文章中已經說過了, 直接看代碼
package main
import "fmt"
type Person struct {
age int
name string
sex bool
}
type MPerson struct {
*Person
int
}
func main() {
var mperson MPerson = MPerson{&Person{18, "xiaohua", true}, 1}
fmt.Println(*(mperson.Person), mperson.int)
}
同名欄位
- 看代碼
package main
import "fmt"
type Person struct {
age int
name string
sex bool
}
type MPerson struct {
Person
age int
}
func main() {
var mperson MPerson = MPerson{Person{18, "xiaohua", true}, 1}
fmt.Println(mperson)
}
- 可以發現MPerson的age和Person中的age同名, 通過代碼可以發現, 實際上這2個同名欄位并不是一個欄位
- 可以聯系C++的封裝來進行理解
- 那么如果我們現在輸出MPerson的age欄位, 輸出的是哪個age呢?
- 實際上輸出的就是MPerson的age即上面代碼中的1
注:
如果想要輸出MPerson中的Person中的age
可以這么寫:
fmt.Println(mperson.Person.age)
可以發現Go和C++還是有區別的, 此處是Person.age, 而不是Person::age, 注意之
方法
- 方法其實就是面向物件中的成員函式, 先來看代碼
package main
import "fmt"
type Person struct {
age int
id int
}
func (obj Person)f() {
fmt.Println(obj.age)
}
func main() {
person := Person{18, 1}
person.f()
}
注:
func (obj Person)f()
表示這是關聯到Person的方法, 之后的呼叫就可以通過Person物件來通過.來使用該方法
- 事實上, 編譯器內部會幫你自動轉化指標和物件, 哪怕你只有一個針對于物件呼叫的方法, 你用指標去呼叫也是可以的, 如下:
package main
import "fmt"
type Person struct {
age int
id int
}
func (obj Person)f() {
fmt.Println("hello")
}
func main() {
p := &Person{18, 1}
p.f() // 用指標去呼叫也是可以的
}
- 之前說過要想在方法中改struct中的value, 必須傳遞指標, 由于編譯器會幫我們自動轉換, 所以我們看看我們用指標去呼叫針對于物件的方法能不能改
package main
import "fmt"
type Person struct {
age int
id int
}
func (obj Person)f() {
obj.age = 10
}
func main() {
p := &Person{18, 1}
p.f()
fmt.Println(p)
}
注:
答案是改不了
因為編譯器先把p轉化成*p, 再去呼叫f(), 這就是值傳遞語意, 所以改不了, 注意
方法的繼承
- 繼承
package main
import "fmt"
type Person struct {
age int
id int
}
func (obj Person)f() {
fmt.Println("hello")
}
type MPerson struct {
Person
name string
}
func main() {
p := MPerson{Person{18, 1}, "xiaohua"}
p.f()
}
注:
MPerson內部包含了Person的匿名欄位, 不僅繼承了成員, 也繼承了方法
- 重寫
package main
import "fmt"
type Person struct {
age int
id int
}
func (obj Person)f() {
fmt.Println("hello")
}
type MPerson struct {
Person
name string
}
func (obj MPerson)f() {
fmt.Println("hello everyone")
}
func main() {
p := MPerson{Person{18, 1}, "xiaohua"}
p.f()
}
注:
子類重寫了父類的方法, 子類在呼叫的時候默認先從自己的作用域去找方法, 找不到再去父類作用域找
如果想呼叫父類中的同名的方法, 可以這么寫:
p.Person.f()
方法值
- 先看代碼
package main
import "fmt"
type Person struct {
age int
id int
}
func (obj Person)f() {
fmt.Println("hello")
}
func main() {
p := Person{18, 1}
function := p.f
function()
}
- function := p.f這句話其實是保存了struct中方法的入口, 可以類似于函式指標來理解
方法運算式
- 看代碼
package main
import "fmt"
type Person struct {
age int
id int
}
func (obj Person)f() {
fmt.Println("hello")
}
func main() {
p := Person{18, 1}
function := (Person).f
function(p)
}
- function := (Person).f就是方法運算式, 之后呼叫的時候需要傳遞對應的變數進去
- 這個時候我要是想傳遞指標進去還可以像之前那樣成功呼叫嘛?答案是不行的,這里是嚴格的型別檢查
介面
- 介面型別是一種抽象的型別, 它不會暴露出自己所代表的物件的內部的結構和方法集合, 因此介面型別不能實體化
- 介面只有方法宣告, 沒有方法實作, 沒有資料欄位
- 介面的定義類似于:
type 介面的型別名 interface {
方法名
}
- 介面型別的變數可以被任何實作了內部方法的物件來賦值, 其實, Go的介面實作了類似于C++多型的功能
package main
import "fmt"
type Inter interface {
f(data int)
}
type Person struct {
age int
name string
}
func (person Person)f(data int) {
fmt.Println(data)
}
func main() {
var myinterface Inter
p := Person{18, "xiaohua"}
myinterface = p
myinterface.f(10)
}
注:
如果想將物件賦值給介面, 必須這個物件實作了介面的全部方法, 否則會報錯
- 介面的繼承, 在介面中加被繼承的介面的匿名欄位即可
type Inter interface {
f(data int)
}
type Int2er interface {
Inter
f2()
}
注:
在這段代碼中, Inter就是子集, Int2er就是超集
超集可以轉化成子集, 反過來錯誤
可以用C++的多型來類比理解,
超集理解成子類, 子集理解成父類, 轉化可以理解成賦值操作
空介面
- 空介面, 顧名思義, 就是內部什么方法都沒有, 但是空介面可以接收任意型別的物件值, 見代碼
package main
import "fmt"
func main() {
var i interface{} = 1
fmt.Println(i)
i = "hello"
fmt.Println(i)
}
注:
可見, 空介面可以接收任意型別的值
- 空介面的用處: 由于空介面可以接收任意型別的值, 所以和可變引數結合后會有奇效
package main
import "fmt"
func print(args... interface{}) {
for _, data := range args {
fmt.Println(data)
}
}
func main() {
print(1, "hello")
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/252713.html
標籤:其他
