当前位置: 首页 > news >正文

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

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

封装

属性

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

type Student struct {id uintname stringmale boolscore 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 mainimport "fmt"
type Student struct {id    uintname  stringscore 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 anitype 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 anitype Dog struct {AnimalAlias string
}func (d Dog) GetAliasName() string {return d.Alias
}

main.go中如下所示

package mainimport ("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 anitype Dog struct {AnimalAlias 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())

相关文章:

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

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

【网络原理9】HTTP响应篇

在前两篇文章当中,已经分别介绍了HTTP是什么,以及常见的请求头当中的属性。【网络原理7】认识HTTP_革凡成圣211的博客-CSDN博客HTTP抓包,Fiddler的使用https://blog.csdn.net/weixin_56738054/article/details/129148515?spm1001.2014.3001.…...

SpringCloud之Seata(二)

4.Seata如何应用于项目? 安装seata及修改配置 4.1 官网下载Seata安装包 4.2 修改seata/config.txt 4.2.1 修改存储方式 store.db.dbTypemysql store.db.driverClassNamecom.mysql.jdbc.Driver store.db.urljdbc:mysql://你的IP:3306/seata?useUnicodetrue sto…...

【Redis-入门阶段】基本数据结构

Redis支持多种数据结构,包括字符串、列表、哈希、集合和有序集合。这些数据结构在Redis中被称为键值对,其中键是一个字符串,值可以是一个字符串、列表、哈希、集合或有序集合。接下来,我们将详细介绍这些数据结构的使用方法。字符…...

BACnet协议详解————MS/TP物理层,数据链路层和网络层

文章目录写在前面1 物理层2 数据链路层MSTP的流程如下noteMS/TP帧格式3 网络层写在前面 这周加更一篇,来弥补一下之前落下的进度。简单的说两句,之前讲应用层的时候,只是跟官方的手册来同步一下,但是从个人理解来说,自…...

Tomcat

Tomcat 1 简介 1.1 什么是Web服务器 Web服务器是一个应用程序(软件),对HTTP协议的操作进行封装,使得程序员不必直接对协议进行操作,让Web开发更加便捷。主要功能是"提供网上信息浏览服务"。 Web服务器是安…...

创客匠人直播:构建公域到私域的用户增长模型

进入知识付费直播带货时代,很多拥有知识技能经验的老师和培训机构吃到了流量红利。通过知识付费直播,老师们可以轻松实现引流、变现,还可以突破时间、地域的限制,为全国各地的学员带来优质的教学服务,因此越来越受到教…...

机试指南

文章目录零、绪论和IDE安装int取值范围常犯的编程小错误一、枚举和模拟 (暴力求解)(一) 枚举1.Reverse函数 求 反序数2.程序出错的原因1.编译错误 (compile):基本语法错误2.链接错误 (link):函数名写错了3.运行错误 (run):结果与预期不符&…...

Android CTA认证设定首选网络类型

需求 硬件只支持4G,过CTA认证时打网络电话,会出现3G网络的选择,会导致过不了,需要禁用3G网络选择功能。 Android 8.1.0 分析 可adb命令查看当前的网络类型 getprop | grep “network” 打印如下: [gsm.network.type]: [LTE,LTE] [ro.telephony.default_network]: [9] …...

Android 动态切换应用图标方案

经常听到大家讨论类似的需求,怀疑大厂是不是用了此方案,据我个人了解,多数头部 app 其实都是发版来更新节假日的 icon。当然本方案也是一种可选的方案,以前我也调研过,存在问题和作者所述差不多,此外原文链…...

SMART PLC斜坡函数功能块(梯形图代码)

斜坡函数Ramp的具体应用可以参看下面的文章链接: PID优化系列之给定值斜坡函数(PLC代码+Simulink仿真测试)_RXXW_Dor的博客-CSDN博客很多变频器里的工艺PID,都有"PID给定值变化时间"这个参数,这里的给定值变化时间我们可以利用斜坡函数实现,当然也可以利用PT1…...

不那么认真的linux复习

这是个不那么认真的linux总结,可能有一些错误 1、linuxkernel(内核)shell(外壳)fs(文件系统)pro/uti/tol(应用程序) 2、ls(列出文件) -a&#xf…...

Redis系列文章总纲

跟着老万学Redis 前言 从事开发工作这么久,很多核心技术其实都还只是局限在满足日常开发工作中的基础使用,并没有完整的总结研究。今年的目标之一是完成几个技术栈的系列博客,系统的总结一下知识体系,目前计划是从Redis开始。 Re…...

更新丨三大模块升级,助力高效交付商业项目!

功能更新!本文将介绍最新升级的步进漫游、行业方案、VR漫游三个模块,让您更快更好的了解系统能力,为您带来更加便捷、高效的使用体验。步进漫游 离线导出步进式漫游系统,是基于全景图自动生成三维建模的解决方案,实现大…...

C++回顾(二)——const和引用

2.1 C中的const 2.1.1 C与C中const的比较 (1)C语言中的const C语言中 const修饰的变量是一个 常变量,本质还是变量,有自己的地址空间。 (2)C中的const 1、C中 const 变量声明的是一个真正的常量&#xff…...

MXNet中使用双向循环神经网络BiRNN对文本进行情感分类<改进版>

在上一节的情感分类当中,有些评论是负面的,但预测的结果是正面的,比如,"this movie was shit"这部电影是狗屎,很明显就是对这部电影极不友好的评价,属于负类评价,给出的却是positive。…...

DNS 域名解析

介绍域名 网域名称(英语:Domain Name,简称:Domain),简称域名、网域。 域名是互联网上某一台计算机或计算机组的名称。 域名可以说是一个 IP 地址的代称,目的是为了便于记忆。例如&#xff0c…...

Spring MVC 源码- ViewResolver 组件

ViewResolver 组件ViewResolver 组件,视图解析器,根据视图名和国际化,获得最终的视图 View 对象回顾先来回顾一下在 DispatcherServlet 中处理请求的过程中哪里使用到 ViewResolver 组件,可以回到《一个请求响应的旅行过程》中的 …...

【Hello Linux】初识冯诺伊曼体系

作者:小萌新 专栏:Linux 作者简介:大二学生 希望能和大家一起进步! 本篇博客简介:简单介绍冯诺伊曼体系 冯诺伊曼体系 冯诺伊曼体系结构的合理性 我们在Linux的第一篇博客中讲解了第一台计算机的发明是为了解决导弹的…...

mysql索引,主从多个核心主题去探索问题。

网上收集不错的优化方案 事务 mvcc 详讲 详讲 索引 索引概念 MySQL官方对索引的定义为:索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据 库系统还维护者满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据, 这样就可以在这些数 据…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...

【Oracle APEX开发小技巧12】

有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

LabVIEW双光子成像系统技术

双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制&#xff0c;展现出显著的技术优势&#xff1a; 深层组织穿透能力&#xff1a;适用于活体组织深度成像 高分辨率观测性能&#xff1a;满足微观结构的精细研究需求 低光毒性特点&#xff1a;减少对样本的损伤…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...