over-golang/01-基础语法/12-接口类型-2-断言与多态.md
2021-07-02 18:11:59 +08:00

4.2 KiB
Raw Permalink Blame History

一 断言

接口是编程的规范,他也可以作为函数的参数,以让函数更具备适用性。在下列示例中,有三个接口动物接口、飞翔接口、游泳接口,两个实现类鸟类与鱼类:

  • 鸟类:实现了动物接口,飞翔接口
  • 鱼类:实现了动物接口,游泳接口
package main

import "fmt"

// 定义一个通用接口:动物接口
type Animal interface {
	Breath()			// 动物都具备 呼吸方法
}

type Flyer interface {
	Fly()
}
type Swimer interface {
	Swim()
}

// 定义一个鸟类:其呼吸的方式是在陆地
type Bird struct {
	Name string
	Food string
	Kind string
}
func (b *Bird) Breath() {
	fmt.Println("鸟 在 陆地 呼吸")
}
func (b *Bird) Fly() {
	fmt.Printf("%s 在 飞\n", b.Name)
}

// 一定一个鱼类:其呼吸方式是在水下
type Fish struct {
	Name string
	Kind string
}
func (f *Fish) Breath() {
	fmt.Println("鱼 在 水下 呼吸")
}
func (f *Fish) Swim() {
	fmt.Printf("%s 在游泳\n", f.Name)
}

// 一个普通函数,参数是动物接口
func Display(a Animal) {
	// 直接调用接口中的方法
	a.Breath()
	// 调用实现类的成员:此时会报错
	fmt.Println(a.Name)
}

func main() {
	var b = &Bird{
		"斑鸠",
		"蚂蚱",
		"鸟类"
	}
	Display(b)
}

接口类型无法直接访问其具体实现类的成员需要使用断言type assertions对接口的类型进行判断类型断言格式

t := i.(T)			//不安全写法如果i没有完全实现T接口的方法这个语句将会触发宕机
t, ok := i.(T)		// 安全写法如果接口未实现接口将会把ok掷为falset掷为T类型的0值
  • i代表接口变量
  • T代表转换的目标类型
  • t代表转换后的变量

上述案例的Dsiplay就可以书写为

func Display(a Animal) {
	// 直接调用接口中的方法
	a.Breath()
	// 调用实现类的成员:此时会报错
	instance, ok := a.(*Bird)	// 注意:这里必须是 *Bird类型因为是*Bird实现了接口不是Bird实现了接口
	if ok {
		// 得到了具体的实现类,才能访问实现类的成员
		fmt.Println("该鸟类的名字是:", instance.Name)
	} else {
		fmt.Println("该动物不是鸟类")
	}
}

二 接口类型转换

在接口定义时其类型已经确定因为接口的本质是方法签名的集合如果两个接口的方法签名结合相同顺序可以不同则这2个接口之间不需要强制类型转换就可以相互赋值因为go编译器在校验接口是否能赋值时比较的是二者的方法集。

在上一节中函数Display接收的是Animal接口类型在断言后转换为了别的类型*Bird(实现类指针类型)

func Display(a Animal) {
	instance, ok := a.(*Bird)		// 动物接口转换为了 *Bird实现类
	if ok {
		// 得到了具体的实现类,才能访问实现类的成员
		fmt.Println("该鸟类的名字是:", instance.Name)
	} else {
		fmt.Println("该动物不是鸟类")
	}
}

其实,断言还可以将接口转换成另外一个接口:

func Display(a Animal) {
	instance, ok := a.(Flyer)			// 动物接口转换为了飞翔接口
	if ok {
		instance.Fly()
	} else {
		fmt.Println("该动物不会飞")
	}

}

一个实现类往往实现了很多接口为了精准类型查询可以使用switch语句来判断对象类型

var v1 interfaceP{} = ...
switch v := v1.(type) {
	case int:
	case string:
	...
}

三 多态

多态是面向对象的三大特性之一,即一个类型具备多种具体的表现形式。

上述示例中,鸟和鱼都实现了动物接口的 Breath方法即动物的Breath方法在鸟和鱼中具备不同的体现。我们在new出动物的具体对象实例时这个对象实例也就实现了对应自己的接口方法。

// New出Animal的函数
func NewAnimal(kind string) Animal{

	switch kind {
	case "鸟类":
		return &Bird{}
	case "鱼类":
		return &Fish{}
	default:
		return nil
	}

}

func main() {
	// 获取的是动物接口类型,但是实现类是鸟类
	a1 := NewAnimal("鸟类")
	a1.Breath()		// 鸟 在 陆地 呼吸

	// 获取的是动物接口类型,但是实现类是鱼类
	a2 := NewAnimal("鱼类")
	a2.Breath()		// 鱼 在 水下 呼吸
}