GoLang也提供了繼承的方法,不過是通過匿名組合的方式來實現(xiàn)的谜洽。
1. 非指針繼承
基本語法
// 基類
type Base struct {
// 成員變量
}
func (b *Base) 函數(shù)名(參數(shù)列表) (返回值列表) {
// 函數(shù)體
}
// 派生類
type Derived struct {
Base //匿名:只有type萝映,沒有變量名
// 成員變量
}
func (b *Derived) 函數(shù)名(參數(shù)列表) (返回值列表) {
// 函數(shù)體
}
繼承規(guī)則
1. 在派生類沒有改寫基類的成員方法時,相應的成員方法被繼承阐虚。
2. 派生類可以直接調用基類的成員方法序臂,譬如基類有個成員方法為Base.Func(),那么Derived.Func()等同于Derived.Base.Func()
3. 倘若派生類的成員方法名與基類的成員方法名相同实束,那么基類方法將被覆蓋或叫隱藏奥秆,譬如基類和派生類都有成員方法Func()逊彭,那么Derived.Func()將只能調用派生類的Func()方法,如果要調用基類版本吭练,可以通過Derived.Base.Func()來調用诫龙。
示例
package main
import "fmt"
type Base struct {
}
func (b *Base) Func1() {
fmt.Println("Base.Func1() was invoked!")
}
func (b *Base) Func2() {
fmt.Println("Base.Func2() was invoked!")
}
type Derived struct {
Base
}
func (d *Derived) Func2() {
fmt.Println("Derived.Func2() was invoked!")
}
func (d *Derived) Func3() {
fmt.Println("Derived.Func3() was invoked!")
}
func main() {
d := &Derived{}
d.Func1() // Base.Func1() was invoked!
d.Base.Func1() // Base.Func1() was invoked!
d.Func2() // Derived.Func2() was invoked!
d.Base.Func2() // Base.Func2() was invoked!
d.Func3() // Derived.Func3() was invoked!
}
2. 指針方式組合
基本語法
// 基類
type Base struct {
// 成員變量
}
func (b *Base) 函數(shù)名(參數(shù)列表) (返回值列表) {
// 函數(shù)體
}
// 派生類
type Derived struct {
*Base
// 成員變量
}
func (b *Derived) 函數(shù)名(參數(shù)列表) (返回值列表) {
// 函數(shù)體
}
繼承規(guī)則
1. 基類采用指針方式的組合析显,依然具有派生的效果鲫咽,只是派生類創(chuàng)建實例的時候需要外部提供一個基類實例的指針。
2. 其他規(guī)則與非指針方式組合一致谷异。
示例
package main
import (
"fmt"
"log"
"os"
)
type MyJob struct {
Command string
*log.Logger
}
func (job *MyJob) Start() {
job.Println("job started!") // job.Logger.Println
fmt.Println(job.Command)
job.Println("job finished!") // job.Logger.Println
}
func main() {
logFile, err := os.OpenFile("./job.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0)
if err != nil {
fmt.Println("%s", err.Error())
return
}
defer logFile.Close()
logger := log.New(logFile, "[info]", log.Ldate|log.Ltime|log.Llongfile)
job := MyJob{"programming", logger}
job.Start()
job.Println("test finished!") // job.Logger.Println
}
3. 其他
名字覆蓋
// 上面說明了派生類成員方法名與基類成員方法名相同時基類方法將被覆蓋的情況分尸,這對于成員變量名來說,規(guī)則也是一致的歹嘹。
// package main
import "fmt"
type Base struct {
Name string
}
type Base1 struct {
Name string
}
type Derived struct {
Base
Base1
Name string
}
func main() {
d := &Derived{}
d.Name = "Derived"
d.Base.Name = "Base"
fmt.Println(d.Name) // Derived
fmt.Println(d.Base.Name) // Base
}
名字沖突
// 匿名組合相當于以其類型名稱(去掉包名部分)作為成員變量的名字箩绍。
// 那么按此規(guī)則,類型中如果存在兩個同名的成員尺上,即使類型不同材蛛,但我們預期會收到編譯錯誤。
package main
import "log"
type Logger struct {
Level int
}
type MyJob struct {
*Logger
Name string
*log.Logger // duplicate field Logger
}
func main() {
job := &MyJob{}
}
// 報錯
reflect.StructOf: duplicate field X
// 同一深度怎抛,且沒有被覆蓋卑吭,直接調用d.Name還是會沖突
// 但是可以使用d.Base.Name
package main
import "fmt"
type Base struct {
Name string
}
type Base1 struct {
Name string
}
type Derived struct {
Base
Base1
// Name string
}
func main() {
d := &Derived{}
d.Name = "Derived"
d.Base.Name = "Base"
fmt.Println(d.Name) // Derived
fmt.Println(d.Base.Name) // Base
}
ambiguous selector d.Name