Golang的继承模拟实例


Posted in Golang onJune 30, 2021

1.前言

面向对象编程的三大特性:封装、继承、多态。可见继承是面向对象程序设计中一个重要的概念。Go 作为面向对象的编程语言,自然也支持继承。

比较特殊的是 Go 实现继承的方式与其他传统 OOP 语言所有不同,不像 C++ 有专门的继承语法,或者像 Java 中有专门的关键字 extends。

C++ 的继承:

// 基类
class Animal {
public:
    void eat(); 
	void sleep();
};


// 子类
class Dog : public Animal {
public:
    void bark();
};

Java 的继承:

// 基类
public class Animal {
	public void eat(){};
	public void sleep(){};
}
// 子类
public class Dog extends Animal {
	public void bark(){};
}

2.嵌入式继承机制

Go 使用匿名嵌套实现继承。

我们用很容易理解的动物-猫来举例子。

type Animal struct {
	Name string
}
func (a *Animal) Eat() {
	fmt.Printf("%v is eating", a.Name)
	fmt.Println()
}
type Cat struct {
	Animal
}
cat := &Cat{
	Animal: Animal{
		Name: "cat",
	},
}
cat.Eat() // cat is eating

首先,我们实现了一个 Animal 的结构体,代表动物类。并声明了 Name 字段,用于描述动物的名字。

然后,实现了一个以 Animal 为 receiver 的 Eat 方法,来描述动物进食的行为。

最后,声明了一个 Cat 结构体,组合了 Cat 字段。再实例化一个猫,调用Eat方法,可以看到会正常的输出。

可以看到,Cat 结构体本身没有 Name 字段,也没有去实现 Eat() 方法。唯一有的就是匿名嵌套的方式继承了 Animal 父类,至此,我们证明了 Go 通过匿名嵌套的方式实现了继承。

上面是嵌入类型实例,同样地也可以嵌入类型指针。

type Cat struct {
	*Animal
}
cat := &Cat{
	Animal: &Animal{
		Name: "cat",
	},
}

3.嵌入式继承机制的的局限

相比于 C++ 和 Java, Go 的继承机制的作用是非常有限的,因为没有抽象方法,有很多的设计方案可以在 C++ 和 Java 中轻松实现,但是 Go 的继承却不能完成同样的工作。

package main
import "fmt"
// Animal 动物基类
type Animal struct {
	name string
}
func (a *Animal) Play() {
	fmt.Println(a.Speak())
}
func (a *Animal) Speak() string {
	return fmt.Sprintf("my name is %v", a.name)
}
func (a *Animal) Name() string {
	return a.name
}
// Dog 子类狗
type Dog struct {
	Animal
	Gender string
}
func (d *Dog) Speak() string {
	return fmt.Sprintf("%v and my gender is %v", d.Animal.Speak(), d.Gender)
}
func main() {
	d := Dog{
		Animal: Animal{name: "Hachiko"},
		Gender:  "male",
	}
	fmt.Println(d.Name())
	fmt.Println(d.Speak())
	d.Play() // Play() 中调用的是基类 Animal.Speak() 方法,而不是 Dog.Speak()
}

运行输出:

Hachiko
my name is Hachiko and my gender is male
my name is Hachiko

上面的例子中,Dog 类型重写了 Speak() 方法。然而如果父类型 Animal 有另外一个方法 Play() 调用 Speak() 方法,但是 Dog 没有重写 Play() 的时候,Dog 类型的 Speak() 方法则不会被调用,因为 Speak() 方法不是抽象方法,此时继承无法实现多态。

4.使用接口封装方法

为了解决上面的问题,我们应该使用接口封装方法,通过实现接口方法来实现多态。

package main
import (
    "fmt"
)
type Animal interface {
    Name() string
    Speak() string
    Play()
}
type Dog struct {
    name string
    gender string
}
func (d *Dog) Play() {
    fmt.Println(d.Speak())
}
func (d *Dog) Speak() string {
    return fmt.Sprintf("my name is %v and my gender is %v", d.name, d.gender)
}
func (d *Dog) Name() string {
    return d.name
}
func Play(a Animal) {
    a.Play()
}
func main() {
    d :=&Dog{"Hachiko", "male"}
    fmt.Println(d.Name())
    fmt.Println(d.Speak())
    Play(d)
}

运行输出:

Hachiko
my name is Hachiko and my gender is male
my name is Hachiko and my gender is male

注意:Go 中某个类型需要实现接口中的所有方法才算作实现了接口。

5.小结

如果一个 struct 嵌套了另一个匿名结构体,那么这个结构可以直接访问匿名结构体的属性和方法,从而实现继承。

如果一个 struct 嵌套了另一个有名的结构体,那么这个模式叫做组合。

如果一个 struct 嵌套了多个匿名结构体,那么这个结构可以直接访问多个匿名结构体的属性和方法,从而实现多重继承。

本篇文章就到这里了,希望能帮助到你,也希望您能多多关注三水点靠木的更多内容!

Golang 相关文章推荐
为什么不建议在go项目中使用init()
Apr 12 Golang
go 原生http web 服务跨域restful api的写法介绍
Apr 27 Golang
go设置多个GOPATH的方式
May 05 Golang
golang switch语句的灵活写法介绍
May 06 Golang
goland 设置project gopath的操作
May 06 Golang
Go 自定义package包设置与导入操作
May 06 Golang
Go 在 MongoDB 中常用查询与修改的操作
May 07 Golang
Go 语言结构实例分析
Jul 04 Golang
go使用Gin框架利用阿里云实现短信验证码功能
Aug 04 Golang
Go并发4种方法简明讲解
Apr 06 Golang
Golang 入门 之url 包
May 04 Golang
深入理解 Golang 的字符串
May 04 Golang
go select编译期的优化处理逻辑使用场景分析
Go 语言下基于Redis分布式锁的实现方式
Jun 28 #Golang
go语言使用Casbin实现角色的权限控制
Go语言设计模式之结构型模式
浅谈Go语言多态的实现与interface使用
Jun 16 #Golang
再次探讨go实现无限 buffer 的 channel方法
Jun 13 #Golang
Go遍历struct,map,slice的实现
Jun 13 #Golang
You might like
百度工程师讲PHP函数的实现原理及性能分析(三)
2015/05/13 PHP
php使用curl伪造来源ip和refer的方法示例
2018/05/08 PHP
用js判断用户浏览器是否是XP SP2的IE6
2007/03/08 Javascript
jQuery 研究心得 取得属性的值
2007/11/30 Javascript
JavaScript Date对象使用总结
2009/05/14 Javascript
js文件缓存之版本管理详解
2013/07/05 Javascript
JavaScript函数作用域链分析
2015/02/13 Javascript
MVVM模式中ViewModel和View、Model有什么区别?
2015/06/19 Javascript
javascript拖拽效果延伸学习
2016/04/04 Javascript
深入理解js数组的sort排序
2016/05/28 Javascript
原生js实现class的添加和删除简单代码
2016/07/12 Javascript
js事件源window.event.srcElement兼容性写法(详解)
2016/11/25 Javascript
完美解决spring websocket自动断开连接再创建引发的问题
2017/03/02 Javascript
浅谈Vue父子组件和非父子组件传值问题
2017/08/22 Javascript
JS组件系列之Gojs组件 前端图形化插件之利器
2017/11/29 Javascript
vue.js删除列表中的一行
2018/06/30 Javascript
vue实现微信分享链接添加动态参数的方法
2019/04/29 Javascript
Javascript Worker子线程代码实例
2020/02/20 Javascript
用Python中的wxPython实现最基本的浏览器功能
2015/04/14 Python
python文件的md5加密方法
2016/04/06 Python
Python中的time模块与datetime模块用法总结
2016/06/30 Python
python GUI库图形界面开发之PyQt5浏览器控件QWebEngineView详细使用方法
2020/02/26 Python
学python爬虫能做什么
2020/07/29 Python
selenium与xpath之获取指定位置的元素的实现
2021/01/26 Python
HTML5样式控制示例代码
2013/11/27 HTML / CSS
html5中valid、invalid、required的定义
2014/02/21 HTML / CSS
科颜氏美国官网:Kiehl’s美国
2017/01/31 全球购物
美国机场停车位预订:About Airport Parking
2018/03/26 全球购物
加拿大在线眼镜零售商:SmartBuyGlasses加拿大
2019/05/25 全球购物
企业标语口号
2014/06/10 职场文书
2014年大学团支部工作总结
2014/12/02 职场文书
同学聚会通知短信
2015/04/20 职场文书
现货白银电话营销话术
2015/05/29 职场文书
Vue3如何理解ref toRef和toRefs的区别
2022/02/18 Vue.js
vue中this.$http.post()跨域和请求参数丢失的解决
2022/04/08 Vue.js
Go语言安装并操作redis的go-redis库
2022/04/14 Golang