1. 程式人生 > 實用技巧 >Go 其六 過載,重寫,覆蓋 && DuckType 補充

Go 其六 過載,重寫,覆蓋 && DuckType 補充

關於過載,重寫,覆蓋的基本概念要分清楚,
  Go中確實是不支援過載的, 官方給出的解釋是
其他語言的經驗告訴我們,有各種同名但簽名不同的方法有時是有用的,但在實踐中也可能令人困惑。關於過載運算子,似乎更方便,但是同樣的,沒有過載會更簡單。
因此這麼設計的目的其實 使Go語言保持簡單 這一核心目標


  而關於重寫和覆蓋,emmm,筆者自己的理解是,畢竟不是真正的繼承,而是複合。並且也不能像繼承一樣用父類定義而使用子類的例項充當具體實現,所以這個問題其實是不存在的?
畢竟 var cat Pet = new(Cat) 是會報錯的,沒有繼承又何談重寫或覆蓋呢?

關於這一部分的程式碼測試例子如下:

package extension

import (
	"testing"
	"fmt"
)

type Pet struct{
}

func (p *Pet) Speak() {
	fmt.Print("In Pet ...")
}

// Go中不支援過載 以下程式碼會提示錯誤:
/*
	method redeclared: Pet.Speak
	method(*Pet) func()
	method(*Pet) func() stringgo
*/
// func (p *Pet) Speak() string {
// 	fmt.Print("In Pet ...")
// 	return "Try to overload"
// }

func (p *Pet) SpeakTo(host string) {
	p.Speak()
	fmt.Println("In Pet ", host)
}

type Dog struct {
	p *Pet
}

func (d *Dog) Speak() {
	fmt.Print("In Dog ...")
	d.p.Speak()
}

func (d *Dog) SpeakTo(host string) {
	d.Speak()
	fmt.Println("In Dog ", host)
	d.p.SpeakTo(host)
}

type Cat struct{
	Pet
}

func (c *Cat) Speak() {
	fmt.Print("Try to overwrite Miao")
}

func TestDog(t *testing.T){
	dog := new(Dog)
	dog.SpeakTo("Hello")

	//注意上面是複合不是整合

	//注意還有Go中的匿名巢狀方法,感覺上像是繼承
	/*
		type Cat struct {
			Pet
		}
	*/

	cat := new(Cat)
	cat.SpeakTo("Miao")
	//輸出結果是In Pet ...In Pet  Miao(Pet中的方法)

	//可以通過
	var cat1 *Pet = new(Pet)
	//下面一行會提示錯誤:cannot use new(Cat) (type *Cat) as type *Pet in assignmentgo
	//var cat1 *Pet = new(Cat)
	cat1.Speak()


	//不同的寫法也是一樣的
	//可以通過
	var cat2 Pet = Pet{}
	//提示錯誤:cannot use Cat literal (type Cat) as type Pet in assignment
	//var cat2 Pet = Cat{}

	cat2.Speak()
}

   輸出結果為:

=== RUN   TestDog
In Dog ...In Pet ...In Dog  Hello
In Pet ...In Pet  Hello
In Pet ...In Pet  Miao
In Pet ...In Pet ...--- PASS: TestDog (0.00s)
=== RUN   TestPolymorphism
*extension.GoProgrammer, fmt.Println("Hello World!")
extension.JavaProgrammer, System.out.Println("Hello World!")
--- PASS: TestPolymorphism (0.00s)
PASS
coverage: [no statements]
ok  	Session12/extension	0.276s	coverage: [no statements]

 

關於DuckType還有一些補充內容:

package extension

import (
	"testing"
	"fmt"
)

type Code string
type Programmer interface{
	WriteHelloWorld() Code
	//ReadHelloWorld() Code
	//上面這個ReadHelloWorld() Code是一個實驗
	/*
		如果一個介面沒有實現介面的全部方法會怎樣呢?
			如果我們在接口裡定義了ReadHelloWorld() Code這個方法,即使後面我們完全沒有用到它
			仍然會在build時提示錯誤:cannot use goProg (type *GoProgrammer) as type Programmer in argument to writeFirstProgram:
				*GoProgrammer does not implement Programmer (missing ReadHelloWorld method)
			
		也就是說,雖然有Duck Type的存在,但如果一個結構沒有實現介面的全部方法,那麼這個結構就不能作為這個介面的實現。
	*/
}

type GoProgrammer struct{
}

func (p *GoProgrammer) WriteHelloWorld() Code {
	return "fmt.Println(\"Hello World!\")"
}

type JavaProgrammer struct{
}

func (p JavaProgrammer) WriteHelloWorld() Code {
	return "System.out.Println(\"Hello World!\")"
}

func writeFirstProgram(p Programmer){
	fmt.Printf("%T, %v\n", p, p.WriteHelloWorld())
}

func TestPolymorphism(t *testing.T){
	goProg := new(GoProgrammer)
	javaProg := JavaProgrammer{}
	writeFirstProgram(goProg)
	writeFirstProgram(javaProg)

	//Interface不只能傳遞指標型別,而是根據例項有沒有實現interface的方法,例如上面的javaProg
	//例如上面如果把 JavaProgram中的WriteHelloWorld方法定義更改為:func (p *JavaProgrammer) WriteHelloWorld() Code
	//則會提示錯誤:cannot use javaProg (type JavaProgrammer) as type Programmer in argument to writeFirstProgram:
	//JavaProgrammer does not implement Programmer (WriteHelloWorld method has pointer receiver)
}

 

  總結一下就是,雖然有DuckType存在,不需要繼承自介面, 但如果一個結構沒有實現介面的全部方法,它仍然不能看作是這個介面的實現。

  另外Interface能不能傳遞指標型別,主要看例項實現介面時的方式。