您的位置:首页 > 科技 > 能源 > 多态与接口(Golang)

多态与接口(Golang)

2024/12/23 9:55:00 来源:https://blog.csdn.net/Hock2023/article/details/141901788  浏览:    关键词:多态与接口(Golang)

多态实现的基本要求

  1. 有一个父类(有接口)
  2. 有子类实现了父类的全部方法
  3. 父类类型的变量(指针)指向(引用)子类的具体实现数据变量

案例

案例1:父类实现的是子类的共性

type Animal interface {Yell() //叫声Eat()SizePrint()
}//对于一个接口而言 没有所谓的继承 而是实现了这个接口所有方法的类 就可以使用这个接口// 下面 我创建一个案例 来实现三种动物的叫声 进食和体型
type Dog struct {Color stringSize  stringSound stringFood  string
}
type Cat struct {Food  stringSize  stringSound stringLike  string
}
type Pig struct {Food    stringSize    stringSound   stringAbility string
}// 接下来 我们来实现猫的方法
func (c Cat) Eat() {fmt.Println("猫猫爱吃", c.Food)
}
func (c Cat) SizePrint() {fmt.Println("猫猫的体型是", c.Size)
}
func (c Cat) Yell() {fmt.Println(c.Sound)
}// 接下来 我们来实现狗的方法
func (d *Dog) Yell() {println(d.Sound)
}
func (d *Dog) Eat() {println("狗狗吃", d.Food)
}
func (d *Dog) SizePrint() {fmt.Println("狗狗的体型是", d.Size)
}// 接下来 我们来实现猪的方法
func (p *Pig) Yell() {println(p.Sound)
}
func (p *Pig) Eat() {println("猪猪吃", p.Food)
}
func (p *Pig) SizePrint() {fmt.Println("狗狗的体型是", p.Size)
}func test1() {dog1 := Dog{Color: "red",Size:  "big",Sound: "wangwang",Food:  "bone",} //这里 我们创建的是一个狗的实例 现在我们尝试使用接口类去调用方法var animal Animalanimal = &dog1 //我们可以讲这个借口指针指向dog1这个实例animal.Yell()animal.Eat()animal.SizePrint()animal = &Cat{Food:  "fish",Size:  "small",Sound: "miao",Like:  "mouse",}animal.Yell()animal.Eat()animal.SizePrint()//通过上面的两个案例,我们可以看出,接口指向一个已有实例或者指向一个创建的实例}
  1. 首先,我们注意到,对于一个类的方法而言,我们不需要额外传入这个类的参数,而这个方法直接就可以使用这个类的参数。这点在下面一个案例的多态接口方法中也是如此。
  2. 再者,我们有可以发现,正如上面所说,一个类想要使用某个接口,就需要实现该接口的所有方法
  3. 再者,一个接口(父类)可以通过接收变量和直接赋值两种方式接收子类的地址(更清晰的理解是指针指向子类)

输出结果:

wangwang
狗狗吃 bone       
狗狗的体型是 big  
miao              
猫猫爱吃 fish     
猫猫的体型是 small

案例2:通过接口实现一个多态方法


// 下面 我们实现一下多态的案例
func ShowAnimal(animal Animal) {animal.Yell()animal.Eat()//animal.SizePrint()//fmt.Println("输入的动物体型为", animal.Size) 需要注意的是 这个接口不能直接调用输出类的元素 只能调用方法
}func test2() {dog2 := Dog{Size: "small", Food: "bone", Sound: "wangwang"}cat2 := Cat{Size: "big", Food: "fish", Sound: "miao", Like: "mouse"}//这就实现了一个多态的方法 即同一个方法可以被不同的类调用 (只要这些类属于一个接口)ShowAnimal(&dog2)ShowAnimal(&cat2)
}
  1. ShowAnimal这个方法就是一个父类的方法,我们将子类的地址传进去,就可以实现统一个方法被不同类调用的效果

  2. 值得注意的是ShowAnimal里的两行注释,说明了接口这个可以调用方法,而不能去直接引用这个子类的成员变量。而为了解决这个问题,我们通常使用的方法是构造一个“返回值函数”,案例如下

    // 下面 我们实现一下多态的案例
    type Animal2 interface {Yell()Eat()SizePrint()GetSize() string//解决方案
    }func (d Dog) GetSize() string {return d.Size
    }
    func (c Cat) GetSize() string {return c.Size
    }
    func (p Pig) GetSize() string {return p.Size
    }func ShowAnimal(animal Animal2) {animal.Yell()animal.Eat()//animal.SizePrint()fmt.Println("输入的动物体型为", animal.GetSize())
    }func test2() {dog2 := Dog{Size: "small", Food: "bone", Sound: "wangwang"}cat2 := Cat{Size: "big", Food: "fish", Sound: "miao", Like: "mouse"}//这就实现了一个多态的方法 即同一个方法可以被不同的类调用 (只要这些类属于一个接口)ShowAnimal(&dog2)ShowAnimal(&cat2)
    }
    

    运行结果:

    wangwang
    狗狗吃 bone           
    输入的动物体型为 small
    miao                  
    猫猫爱吃 fish         
    输入的动物体型为 big  
    

运行结果:

wangwang
狗狗吃 bone  
miao         
猫猫爱吃 fish

案例3:一个类可以有多个接口

这和函数的原则是很类似的:每个接口(函数)实现的功能很少,这样可以使这个接口(函数)的使用率提高和适用场景增多。

//接下来 我想说明一点 就是一个类可以属于多个接口 我们拿智能手机来举例子
// 在智能手机中 我们可以实现“电话的功能”(接听 拨打 发信息) 也可以实现“相机的功能”(拍照 录像) 还可以实现视频播放器的功能(看电视剧)type Phone interface {//这个接口有智能手机和移动电话两个类Listen()Call()Message()
}type Camera interface {//这个接口有智能手机和相机两个类TakePhoto()RecordVideo()
}type VideoPlayer interface {//这个接口有智能手机和电视两个类PlayVideo()
}type MobilePhone struct {Sound stringTel   stringText  stringName  string
}
type camera struct {Take   stringRecord stringName   string
}
type TV struct {Play stringName string
}type Smartphone struct {MobilePhonecameraTVName string
}// 下面三个实现的是一个手机类接口的方法
func (m MobilePhone) Listen() {println(m.Name, "发出", m.Sound, "的声响")
}
func (m MobilePhone) Call() {println(m.Name, m.Tel, "可以拨打电话")
}
func (m MobilePhone) Message() {println(m.Name, "可以发送信息,内容为", m.Text)
}// 下面三个实现的是一个相机类接口的方法
func (c camera) TakePhoto() {println(c.Name, "可以", c.Take)
}
func (c camera) RecordVideo() {println(c.Name, "可以", c.Record)
}// 下面三个实现的是一个电视类接口的方法
func (t TV) PlayVideo() {println(t.Name, "可以", t.Play)
}// 下面是三个多态函数
func ShowPhone(phone Phone) {phone.Listen()phone.Call()phone.Message()
} //由于传入参数的限制 这个函数可以被所有手机类调用
func ShowCamera(camera Camera) {camera.TakePhoto()camera.RecordVideo()
}
func ShowTV(TV VideoPlayer) {TV.PlayVideo()
}func test3() {smartphone := Smartphone{MobilePhone: MobilePhone{Sound: "beep", Tel: "123456", Text: "hello", Name: "智能手机"},camera:      camera{Take: "take", Record: "record", Name: "智能手机"},TV:          TV{Play: "play", Name: "智能手机"},//Name:        "智能手机",} //创建一个智能手机实例ShowPhone(&smartphone)ShowCamera(&smartphone)ShowTV(&smartphone)
}
  1. 我们可以发现,只要是父类实现了某个方法,那么子类就可以使用该方法而无需自己实现,但问题就是该方法调用的成员变量只能是子类嵌套里面的(就像在实例里面,我在每个父类中都创建了Name:“智能手机”这个东西)。这个只能说仁者见仁智者见智了,如果是省事减少新类型方法的创建,就直接继承即可,在结构体实例里面略显别扭的写入成员变量;或者可以自己新创建一个方法(需要配套称整个接口),来实现:

    // 智能手机类方案
    type SmartPhone interface {Listen()Call()Message()TakePhoto()RecordVideo()PlayVideo()
    }func (smartphone Smartphone) Listen() {println(smartphone.Name, "发出", smartphone.Sound, "的声响")
    }
    func (smartphone Smartphone) Call() {println(smartphone.Name, smartphone.Tel, "可以拨打电话")
    }// 下面是三个多态函数
    func ShowPhone(phone Phone) {phone.Listen()phone.Call()phone.Message()
    } //由于传入参数的限制 这个函数可以被所有手机类调用
    func ShowCamera(camera Camera) {camera.TakePhoto()camera.RecordVideo()
    }
    func ShowTV(TV VideoPlayer) {TV.PlayVideo()
    }func test3() {smartphone := Smartphone{MobilePhone: MobilePhone{Sound: "beep", Tel: "123456", Text: "hello", Name: "智能手机"},camera:      camera{Take: "take", Record: "record", Name: "智能手机"},TV:          TV{Play: "play", Name: "智能手机"},Name:        "智能手机外部",} //创建一个智能手机实例ShowPhone(&smartphone)ShowCamera(&smartphone)ShowTV(&smartphone)
    }
    

    运行结果:

    智能手机外部 发出 beep 的声响      
    智能手机外部 123456 可以拨打电话       
    智能手机 可以发送信息,内容为 hello
    智能手机 可以 take                 
    智能手机 可以 record               
    智能手机 可以 play   
    

    也就是说,我们创建的这个智能手机接口,会优先使用自己类的方法,其次再去使用继承别人的方法!crazy

案例4:空接口作为万能类型使用

// 下面的函数实现的是一个各种类型都可以调用的函数
func Myfunc(arg interface{}) {fmt.Println("我要和你")fmt.Println(arg)
}func test1() {Myfunc("date")Myfunc(17)Myfunc(13.14)
}

运行结果:

我要和你
date    
我要和你
17      
我要和你
13.14   

在实际应用中,我们通常要根据传入数据的不同类型来进行不同操作。go很贴心的给interface{}配备了“断言”这一功能:

func MyFunc2(arg interface{}) {value, ok := arg.(int) //value用来接收传入类型的值(只有断言成功才能接收到),ok用来接收类型转换的结果是否成功if !ok {fmt.Println("类型转换失败")} else {fmt.Println("类型转换成功")fmt.Printf("传入类型为:%T\n", value)}
}func test2() {MyFunc2("date")MyFunc2(17)MyFunc2(13.14)
}

运行结果:

类型转换失败
类型转换成功   
传入类型为:int
类型转换失败  

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com