go面向对象思想封装继承多态

news2024/10/1 7:37:25

go貌似都没有听说过继承,当然这个继承不像c++中通过class类的方式去继承,还是通过struct的方式,所以go严格来说不是面向对象编程的语言,c++和java才是,不过还是可以基于自身的一些的特性实现面向对象的功能,面向对象三大特性:封装、继承、多态

封装

属性

go是接住结构体struct实现类的声明,比如要定义个学生类,学生类和animal类是面向对象示例介绍中重点,可以通过如下方式实现:

type Student struct {
	id uint
	name string
	male bool
	score float64
}

结构体名或者说类名为 Student,并且包含了 idnamemalescore 四个属性,Go 语言中也不支持构造函数、析构函数,取而代之地,可以通过定义形如 NewXXX 这样的全局函数(首字母大写)作为类的初始化函数

func NewStudent(id uint, name string, male bool, score float64) *Student {
	return &Student{id, name, male, score}
}

在这个函数中,我们通过传入的属性字段对 Student 类进行初始化并返回一个指向该类的指针

在 Go 语言中,未进行显式初始化的变量都会被初始化为该类型的零值,例如 bool 类型的零值为 false,int 类型的零值为 0,string 类型的零值为空字符串,float 类型的零值为 0.0。接着就可以调用了
在这里插入图片描述

package main
import (
	"fmt"
	student2 "student/stu"
)
func main() {
	student := student2.NewStudent(1, "zhiyu",false, 100)
	fmt.Println(student)
}

成员函数

getXXX方法

要为 Go 类定义成员方法,需要在 func 和方法名之间声明方法所属的类型(有的地方将其称之为接收者声明),以 Student 类为例,要为其定义获取 name 值的方法,可以这么做:

func (s Student) GetName() string {
	return s.name
}

这样就可以使用GetName方法获取name属性,通过在函数签名中增加接收者声明的方式定义了函数所归属的类型,这个时候,函数就不再是普通的函数,而是类的成员方法了

setXXX方法

getName是一个只读方法,setName是一个可写的方法,所以需要使用指针的方式进行传参,在类的成员方法中,可以通过声明的类型变量来访问类的属性和其他方法

func (s *Student) SetName(name string) {
    s.name = name
}

可以把接收者类型为指针的成员方法叫做指针方法,把接收者类型为非指针的成员方法叫做值方法,二者的区别在于值方法传入的结构体变量是值类型(类型本身为指针类型除外),因此传入函数内部的是外部传入结构体实例的值拷贝,修改不会作用到外部传入的结构体实例.

另外,需要声明的是,在 Go 语言中,当我们将成员方法 SetName 所属的类型声明为指针类型时,严格来说,该方法并不属于 Student 类,而是属于指向 Student 的指针类型,所以,归属于 Student 的成员方法只是 Student 类型下所有可用成员方法的子集,归属于 *Student 的成员方法才是 Student 类完整可用方法的集合。

在调用值方法和指针方法时,需要记住以下两条准则:

  1. 值方法可以通过指针和值类型实例调用,指针类型实例调用值方法时会自动解引用;
  2. 指针方法只能通过指针类型实例调用,但有一个例外,如果某个值是可寻址的(或者说左值),那么编译器会在值类型实例调用指针方法时自动插入取地址符,使得在此情形下看起来像指针方法也可以通过值来调用

左值和右值

结合上面的两条准则使用示例说明如下:

package main

import "fmt"
type Student struct {
	id    uint
	name  string
	score float64
}

func NewStudent(id uint, name string, score float64) *Student {
	return &Student{id: id, name: name, score: score}
}

func NewStudentV2(id uint, name string, score float64) Student {
	return Student{id: id, name: name, score: score}
}

func (s Student) GetName() string {
	return s.name
}

func (s *Student) SetName(name string) {
	s.name = name
}

func main() {
	s := NewStudent(1, "zhiyu", 100)
	s.SetName("zhiyu-1")       // ok 正常调用指针方法
	fmt.Println(s.GetName()) // ok 指针调用值方法自动解引用: (*s).GetName()

	s2 := NewStudentV2(2, "zhiyu", 90)
	s2.SetName("zhiyu-2")       // ok s2 是可寻址的左值,所以实际调用: (&s2).SetName("zhiyu-2")
	fmt.Println(s2.GetName()) // ok 正常调用值方法

	NewStudent(3, "zhiyu", 80).SetName("zhiyu-3")   // ok 正常调用指针方法
	NewStudentV2(4, "zhiyu", 99).SetName("zhiyu-4") // err 值类型调用指针方法
}

之所以可以直接在 s2 值实例上调用 SetName 指针方法,是因为 s2 是可寻址的,Go 语言底层会自动将 s2 转化为对应的指针类型 &s2,所以真正调用的代码是 (&s2).SetName("zhiyu-2"),而通过 NewStudentV2(…)返回实例调用 SetName 时,则会报错,因为 NewStudentV2(…) 是一个不可以寻址的右值。

所谓左值就是可以出现在赋值等号左边的值,而右值只能出现在赋值等号右边,比如函数返回值、字面量、常量值等。左值可寻址,右值不可寻址。

总结下来,就是一个自定义数据类型的方法集合中仅会包含它的所有「值方法」,而该类型对应的指针类型包含的方法集合才囊括了该类型的所有方法,包括所有「值方法」和「指针方法」,指针方法可以修改所属类型的属性值,而值方法则不能。

值方法|指针方法

有如下情形的考量时,需要将类方法定义为指针方法:

  • 数据一致性:方法需要修改传入的类型实例本身;
  • 方法执行效率:如果是值方法,在方法调用时一定会产生值拷贝,而大对象拷贝代价很大。

通常我们都会选择定义指针方法

继承

Go 虽然没有直接提供继承相关的语法实现,但是通过组合的方式间接实现类似功能,所谓组合,就是将一个类型嵌入到另一个类型,从而构建新的类型结构。使用了Student类介绍封装,接着使用Animal示例继承
在这里插入图片描述

package ani

type Animal struct {
	Name string
}

func (a Animal) Call() string {
	return "动物叫声"
}

func (a Animal) FavorFood() string {
	return "爱吃的食物..."
}

func (a Animal) GetName() string {
	return a.Name
}

所有的动物都有以上的三种方法,现在Dog实现继承Animal,新增一个dog.go,一般会给自家的爱狗起一个别名

package ani

type Dog struct {
	Animal
	Alias string
}

func (d Dog) GetAliasName() string {
   return d.Alias
}

main.go中如下所示

package main

import (
	"animal/ani"
	"fmt"
)

func main() {
	animal := ani.Animal{"旺财"}
	dog := ani.Dog{animal, "旺旺"}

	fmt.Println(dog.GetName())
	fmt.Println(dog.Call())
	fmt.Println(dog.GetAliasName())
}

在这里插入图片描述
这就实现了继承的功能。注意在初始化子类时的顺序和struct中的定义顺序是一致的
在这里插入图片描述

多态

所谓多态就是一个函数的多种形态,比如在子类中定义同名函数来覆盖父类方法,专业属于称之为方法重写。每个动物的喜欢吃的食物和叫声不同,这里实现下Dog重写方法,在dog.go

package ani

type Dog struct {
	Animal
	Alias string
}

func (d Dog) GetAliasName() string {
   return d.Alias
}

func (d Dog) FavorFood() string {
	return "大棒骨"
}

func (d Dog) Call() string {
	return "旺旺"
}

重写go build,并在terminal中在animal根目录下
在这里插入图片描述
当然子类也可以直接调用父类Animal中的方法

fmt.Print(dog.Animal.Call())
fmt.Println(dog.Call())
fmt.Print(dog.Animal.FavorFood())
fmt.Println(dog.FavorFood())

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/370259.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TCP 的演化史-byte stream 和 packet

不想写太多代码,我想直接抄一个 TCP sack 实现,参考了 lwIP TCP,很遗憾:TCP: Implement handling received SACKs 无奈不得不自己实现 sack option 的处理。由于 tso/gso/lro/gro,在软件层面难免遇到下面的情况&#…

Java 如何学习?这份5000页Java学习手册值得拥有,适合零基础自学也适合查漏补缺!

学习技巧 在以前大部分人学习都是先去找本书,先看看,再试,要是不懂了在去网上去查,再在继续啃着书本。但现在向书学习和在网上学习这掌握的效果是不同的,要学会用适合自己的学习方式。 目前的学习要是能看进去书本&a…

【5】linux命令每日分享——touch创建文件

大家好,这里是sdust-vrlab,Linux是一种免费使用和自由传播的类UNIX操作系统,Linux的基本思想有两点:一切都是文件;每个文件都有确定的用途;linux涉及到IT行业的方方面面,在我们日常的学习中&…

飞桨 Tensor 介绍

Tensor 介绍 一、Tensor 的概念介绍 飞桨使用张量(Tensor) 来表示神经网络中传递的数据,Tensor 可以理解为多维数组,类似于 Numpy 数组(ndarray) 的概念。与 Numpy 数组相比,Tensor 除了支持运…

C语言 深度剖析数据在内存中的存储

目录数据类型详细介绍整形在内存中的存储:原码,反码,补码大小端字节序介绍及判断浮点型在内存中的存储解析数据类型详细介绍整形:1.为什么char类型也会归类到整形家族当中去呢?字符存储和表示的时候本质上使用的是ASCI…

【华为OD机试模拟题】用 C++ 实现 - 最大相连男生数(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 货币单位换算(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 选座位(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 停车场最大距离(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 重组字符串(2023.Q1) 【华为OD机试模…

integrationobjects/OPC AE Client ActiveX Crack

使用 OPC AE 客户端 ActiveX 进行快速 OPC 警报和事件客户端编程! OPC AE Client ActiveX包括多个 OPC ActiveX 控件,可以轻松嵌入到最流行的 OLE 容器中。这允许用户与任何 OPC AE 服务器连接并实时检索警报和事件。 这种易于使用的 OPC AE ActiveX 简化…

论文笔记|固定效应的解释和使用

DeHaan E. Using and interpreting fixed effects models[J]. Available at SSRN 3699777, 2021. 虽然固定效应在金融经济学研究中无处不在,但许多研究人员对作用的了解有限。这篇论文解释了固定效应如何消除遗漏变量偏差并影响标准误差,并讨论了使用固…

【C语言进阶】文件的顺序读写、随机读写、文本文件和二进制文件、文件读取结束的判定以及文件缓冲区相关知识

​ ​📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:C语言进阶 🎯长路漫漫浩浩,万事皆有期待 文章目录1.文件操作1.1 概述…

优思学院:《改变世界的机器・精益生产之道》是什么著作?

《改变世界的机器》(The Machine That Changed the World)是一本经典的商业管理书籍,由詹姆斯P温斯顿(James P. Womack)、丹尼尔T琼斯(Daniel T. Jones)和丹尼尔罗斯(Daniel Roos&am…

带组态物联网平台源码 代码开源可二次开发 web MQTT Modbus

物联网IOT平台开发辅助文档 技术栈:JAVA [ springmvc / spring / mybatis ] 、Mysql 、Html 、 Jquery 、css 使用协议和优势: TCP/IP、HTTP、MQTT 通讯协议 1.1系统简介 IOT通用物联网系统平台带组态,是一套面向通用型业务数据处理的系统…

Spring MVC 源码- HandlerAdapter 组件(五)之 HttpMessageConverter

HandlerAdapter 组件HandlerAdapter 组件,处理器的适配器。因为处理器 handler 的类型是 Object 类型,需要有一个调用者来实现 handler 是怎么被执行。Spring 中的处理器的实现多变,比如用户的处理器可以实现 Controller 接口或者 HttpReques…

数据结构预算法之买卖股票的最好时机(三)动态规划

目录:一.题目知识点:动态规划二.动态规划数组思路确定1.dp数组以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例推导dp数组一.题目知识点:动态规划动态规划算法的基本思想是:将待求解的问题分解成若干个相互联…

惠普m1136打印机驱动程序安装教程

惠普m113打印机是一款功能强大的多功能打印机,它能够打印、复印、扫描和传真等。如果你要使用这款打印机,你需要下载并安装驱动程序,以确保它能够在你的计算机上正常工作。在本文中,我们将介绍如何下载和安装惠普m1136打印机驱动程…

Python实现贝叶斯优化器(Bayes_opt)优化支持向量机回归模型(SVR算法)项目实战

说明:这是一个机器学习实战项目(附带数据代码文档视频讲解),如需数据代码文档视频讲解可以直接到文章最后获取。1.项目背景贝叶斯优化器 (BayesianOptimization) 是一种黑盒子优化器,用来寻找最优参数。贝叶斯优化器是…

【华为OD机试模拟题】用 C++ 实现 - 跳格子(2023.Q1)

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…

sklearn学习-朴素贝叶斯(二)

文章目录一、概率类模型的评估指标1、布里尔分数Brier Score对数似然函数Log Loss二、calibration_curve:校准可靠性曲线三、多项式朴素贝叶斯以及其变化四、伯努利朴素贝叶斯五、改进多项式朴素贝叶斯:补集朴素贝叶斯ComplementNB六、文本分类案例TF-ID…

excel核对技巧:这么多数据对比的方法应该够用了

日常工作不时会需要对比数据,查找差异,查找重复值等。有的是对比同一工作表中的数据,有的是对比不同工作表之间的数据。希望接下来介绍的多种Excel数据对比方法,让大家能在不同情况下都能快速完成数据的对比。第一部分&#xff1a…

pytorch入门1--数据操作(张量)

一、张量的定义和变换 1.张量表示一个数值组成的数组,这个数组可能有多个维度。 说明,torch.arange(12)可以得到一个一维的(有几层中括号就是几维数组,注意是层,不是个数),一个最内层的一个中括…

Qt图片定时滚动播放器

目录参考结构PicturePlay.promain.cpppictureplay.hpictureplay.cpppictureplay.ui效果参考 Qt图片浏览器 QT制作一个图片播放器 Qt中自适应的labelpixmap充满窗口后,无法缩小只能放大 可以显示jpg、jpeg、png、bmp。可以从电脑上拖动图到窗口并显示出来或者打开文件…