在聊通用万能类型 interface{} 接口之前,先来谈谈 Golang 中多态的实现与基本要素。
interface 定义抽象方法,子类继承并且实现对应的抽象方法,以达到一个抽象接口有多个不同表现形式的目的,这种现象被称为面向对象的多态。
当我们继承一个类时,需要在 struct 中将父类的名字写下来,如果继承接口的话,则无需将父类的名字写下来,直接将接口包含的抽象方法全部实现即可,此时我们可以直接使用 interface 指针接收(指向)一个子对象。
`package main import "fmt" // 本质是一个指针 type AnimalIF interface { Sleep() GetColor() string // 获取动物的颜色 GetType() string // 获取动物的种类 } // 具体的类 type Cat struct { color string // 猫的颜色 } func (this *Cat) Sleep() { fmt.Println("Cat is Sleep...") } func (this *Cat) GetColor() string { return this.color } func (this *Cat) GetType() string { return "Cat" } type Dog struct { color string } func (this *Dog) Sleep() { fmt.Println("Dog is Sleep...") } func (this *Dog) GetColor() string { return this.color } func (this *Dog) GetType() string { return "Dog" } func showAnimal(animal AnimalIF) { animal.Sleep() // 多态 fmt.Println("color = ", animal.GetColor()) fmt.Println("kind = ", animal.GetType()) } func main() { var animal AnimalIF // 接口的数据类型,父类指针 animal = &Cat{"Green"} animal.Sleep() // 调用的就是 Cat 的 Sleep() 方法 animal = &Dog{"Yellow"} animal.Sleep() // 调用 Dog 的 Sleep() 方法,多态现象 cat := Cat{"Green"} dog := Dog{"Yellow"} showAnimal(&cat) showAnimal(&dog) } `
`go fmt .\main.go go run .\main.go `
image
image
有一个父类(有接口)
有子类(实现了父类的全部接口方法)
父类类型的变量(指针)指向(引用)子类的具体数据变量
通用万能类型 interface{},就是元类,表示空接口,就是所有的 struct 最终都实现了这个空接口。
如 int、string、float32、float64、struct 都实现了 interface{} 空接口,可以使用 interface{} 数据类型引用任意数据类型。
`package main import "fmt" func myFunc(arg interface{}) { fmt.Println("myFunc() is called...") fmt.Println(arg) } type Book struct { auth string } func main() { book := Book{"Golang"} myFunc(book) myFunc(233) myFunc("tonghuaroot") myFunc(3.14) } `
image
通过类型断言机制,interface{} 可以区分引用的底层数据类型。
`package main import "fmt" func myFunc(arg interface{}) { fmt.Println("myFunc() is called...") fmt.Println(arg) value, ok := arg.(string) if !ok { fmt.Println("==========") fmt.Println("arg is not string type") fmt.Println("value = ", value) fmt.Printf("value type is %T \n", value) // 在类型断言机制中,如果 arg 不是 string 类型,则 value 值为一个 string 类型的空值(其他类型也适用) fmt.Println("==========") } else { fmt.Println("arg is string type, value = ", value) fmt.Printf("value type is %T \n", value) } } type Book struct { auth string } func main() { book := Book{"Golang"} myFunc(book) myFunc(233) myFunc("tonghuaroot") myFunc(3.14) } `
image
父类的方法通常也包含了一些功能,子类可以重写父类的方法实现新的功能,接口均为抽象方法,是没有实现的空方法,子类实现该接口时要为这些方法定义对应的功能。如果我们有多态的需求,比如说在调用函数时,形式参数传入接口对象,此时可以依据需求传入不同的子类引用,进而执行不同的功能,这种场景是推荐使用接口与子类这种继承(实现)的,其他情况可以采用父类与子类继承这种模式。
算。
`package main import "fmt" // 本质是一个指针 type AnimalIF interface { Sleep() GetColor() string // 获取动物的颜色 GetType() string // 获取动物的种类 } // 具体的类 type Cat struct { color string // 猫的颜色 } func (this *Cat) Sleep() { fmt.Println("Cat is Sleep...") } func (this *Cat) GetColor() string { return this.color } func (this *Cat) GetType() string { return "Cat" } type Dog struct { color string } func (this *Dog) Sleep() { fmt.Println("Dog is Sleep...") } func (this *Dog) GetColor() string { return this.color } func (this *Dog) GetType() string { return "Dog" } func (this *Dog) PrintMsg() string { return "Hmm..." } func showAnimal(animal AnimalIF) { animal.Sleep() // 多态 fmt.Println("color = ", animal.GetColor()) fmt.Println("kind = ", animal.GetType()) } func main() { var animal AnimalIF // 接口的数据类型,父类指针 animal = &Cat{"Green"} animal.Sleep() // 调用的就是 Cat 的 Sleep() 方法 animal = &Dog{"Yellow"} animal.Sleep() // 调用 Dog 的 Sleep() 方法,多态现象 cat := Cat{"Green"} dog := Dog{"Yellow"} fmt.Println("Dog Print Message: ", dog.PrintMsg()) showAnimal(&cat) showAnimal(&dog) } `
image