【Hello Go】Go语言面向对象

Go语言面向对象

    • 匿名组合 -- 继承
      • 匿名字段
      • 初始化
      • 成员操作
      • 同名字段
      • 其他匿名字段
        • 非结构体类型和指针类型
    • 方法 -- 封装
      • 概述
      • 为类型添加方法
        • 基础类型作为接受者
        • 结构体作为接受者
      • 值语义和引用语义
      • 方法集
        • *T类型方法集
        • 类型T的方法集
      • 匿名字段
        • 方法的继承
        • 方法的重写
      • 表达式
    • 接口 -- 多态
      • 概述
      • 接口的使用
        • 接口定义
        • 接口的实现
      • 接口组合
        • 接口嵌入
        • 接口转换
    • 空接口
      • 类型查询
        • comma-ok 断言
        • switch测试

严格来说 Go语言既不是一个面向对象的语言 也不是一个面向过程的语言

所以说Go语言中并没有封装 继承 多态的概念 但是它通过一些别的方式实现了这些特性

  • 封装: 通过方法实现
  • 继承: 通过匿名字段实现
  • 多态: 通过接口实现

匿名组合 – 继承

匿名字段

一般情况下 定义结构体的时候是字段和类型名是一一对应的 但是实际上Go语言也支持我们只写类型 而不写字段名的方式 被称为嵌入字段

当匿名字段也是一个结构体的时候 那么这个结构体所拥有的全部字段都被隐式的引入了当前这个结构体

type Person struct {
	name string 
	sex byte 
	age int
}


type  Student Struct {
	Person // 匿名字段 默认了Student就包含了Person的所有字段
	id int 
	address string
} 

初始化

这里的初始化其实就是结构体初始化 代码表示如下

	s1 := Student{Person{"mike" , 'm', 18} , 1 , "address"}

成员操作

成员这里也没有什么好说的

我们可以直接通过 . 操作符去访问成员变量

如果是匿名字段 我们可以通过 . 操作符先访问它再访问它的成员变量

同名字段

如果说我们使用嵌入字段的时候有重复的字段

type Person struct {
	name string 
	sex byte 
	age int
}


type  Student struct {
	Person // 匿名字段 默认了Student就包含了Person的所有字段
	name string
	id int 
	address string
} 

那么此时 我们使用 . 赋值就只能给最外层的变量赋值 如果想要给嵌入字段的变量赋值就需要通过 . 操作符先访问该字段

其他匿名字段

非结构体类型和指针类型

所有的内置类型和自定义类型都可以作为匿名字段

声明和初始化方式和上面并无区别

方法 – 封装

概述

在面向对象编程中 一个对象其实也就是一个简单的值或者是一个变量

在这个对象中会包含一些函数 这个都带有接受者的函数 我们称之为方法

本质上 一个方法则是一个和特殊类型关联的函数

一个面向对象的程序会使用方法来表达其属性和对应的操作 这样使用这个对象的用户就不必去直接操作对象 而是使用方法去完成这些操作


在Go语言中 我们可以给任意自定义类型(包括内置类型 但是不包括指针类型) 添加相应的方法

方法总是绑定对象实例 并且隐式的将实例作为第一实参 方法的语法如下

func (receiver ReceiverType) funcName(parameters) (results)
  • 参数 recevier 可以任意命名 如果方法中未使用 则可省略方法名
  • 参数 recevier 类型可以是T或者是 *T
  • 不支持重载方法

为类型添加方法

基础类型作为接受者

我们下面将int 重新命名 之后给他重写了一个方法

之后我们就可以在函数中调用该方法来实现各种功能

package main

import "fmt"

type Myint int

// 在函数定义嵌 在其名字之间放上一个变量 即是一个方法
func (a Myint) Add(b Myint) (result Myint) {
	return a + b
}

func main() {
	var a Myint = 1

	fmt.Println("a.Add(2) = ", a.Add(2)) // 答案是3
}

我们从上面的例子就能看出 面向对象只是换了一种语法形式来表达 方法是函数的语法糖

因为receviver其实就是方法所接受的第一个参数

** 需要注意的是 虽然方法的名字一模一样 但是如果接受者不一样 那么方法就不一样**

结构体作为接受者

方法里面可以访问接收者的字段 调用方法通过点(.)来访问 就像struct里面访问字段一样

type Person struct {
	name string
	sex  byte
	age  int
}

func (P Person) Print()  {
	fmt.Println(P.age , P.name , P.sex)
}

之后如果我们定义了一个Person对象 我们就可以使用Print方法

值语义和引用语义

这里和之前的结构体传参一样 如果我们使用的是值传递 那么就算我们在方法里面修改内部的变量 也不会有什么改变(因为本质就是一个临时拷贝)

如果是指针传递 那么效果就类似于C++中的引用传递

方法集

类型的方法集是指可以被该类型的值调用的所有方法的集合

用实例value 和 pointer 调用方法 不受方法集的约束 编译器总是查找全部方法 并且自动转化为 recevier接受参数

*T类型方法集

一个指向自定义类型的指针 它的方法集是由该类型定义的所有方法组成 无论这些方法接受的是一个值还是一个指针

如果指针上调用一个接受值的方法 那么Go语言会自动将该指针解引用 并且将值作为参数传递给方法的接受者

类型*T方法集包含全部recevierT +*T的方法


类型T的方法集

和指针方法集对应的是 如果我们只有一个值 仍然可以借助于Go语言的传值的地址能力调用指针方法

总结下

  • Go语言很 “聪明” 它知道你调用的方法是传值还是指针的
  • Go语言有值和地址的转化能力 所以说我们无需手动转换

匿名字段

方法的继承

如果说匿名字段实现了一个方法 那么包含这个匿名字段的struct也能调用该方法

type Person struct {
	name string
	sex  byte
	age  int
}

func (P Person) Print()  {
	fmt.Println(P.age , P.name , P.sex)
}

type Student struct{
	Person 
	id int
}

此时我们Student结构体也可以调用Person的方法

方法的重写

由于局部性原则 值和指针会优先访问最近的作用域

所以说如果我们结构体和嵌入字段有相同的方法 那么我们调用该方法时就会优先使用当前结构体的方法

如果我们想要使用嵌入字段的方法 那么我们应该先使用 . 操作符指定该字段 之后在调用方法

表达式

类似于我们可以对于函数进行赋值和传递一样 方法也可以进行赋值和传递

根据调用者不同 方法分为两种形式 方法值和方法表达式 这两者都可以像普通函数那样赋值和传参 区别在于方法值绑定实例 而方法表达式则须显式传参


首先我们来了解下方法表达式和值传参的语法

值传参

pFunc1 := p.PrintInfoPointer //方法值,隐式传递 receiver

方法表达式

pFunc1 := (*Person).PrintInfoPointer

他们的区别就是调用方式

如果我们使用值传参我们可以直接调用

如果是方法表达式 则我们需要传递参数 pFunc1(&p)

接口 – 多态

概述

在Go语言中 接口 是一个自定义类型 接口类型具体描述了一系列方法的集合

接口类型是一种抽象的类型 它不会暴露出自己所代表的对象的值的结构和这个对象支持的基础操作的集合 他们只会展示出自己的方法 因此我们无法实例化接口类型

Go语言通过接口实现了鸭子类型 – 当一只鸟看起来像鸭子 走起来像鸭子 吃起来也像鸭子 那么我们就认为这只鸟是鸭子

我们不关心对象是什么类型 是不是鸭子 只关心行为

接口的使用

接口定义

接口定义的语法如下

type Humaner interface {
	Sayhi()
}
  • 接口命名习惯以er结尾
  • 接口只有方法声明 没有实现 没有数据字段
  • 接口可以匿名嵌入到其他接口 或者结构中
接口的实现

接口是用来定义行为的类型 这些被定义的行为不由接口直接实现 而是通过方法由用户定义的类型实现 一个实现了这些方法的具体类型 就是该接口的实例

如果用户定义的类型 实现了接口类型定义的一组方法 那么这个用户定义类型的值就可以赋给该接口类型的值

这个赋值会把用户定义类型的值存入接口类型的值

type Humaner interface {
	Sayhi()
}

type Student struct {
	name string
	id   int
}

type Teacher struct {
	name string
	id   int
}


// 两个自定类型都实现自己的Sayhi方法
func (s *Student) Sayhi() {
	fmt.Println("student say hi")
}

func (t* Teacher) Sayhi() {
	fmt.Println("teacher say hi")
}


// 普通函数 参数为Humaner类型的变量i
func WhoSayhi(i Humaner) {
	i.Sayhi()
}

func main() {
	t := &Teacher{"mike" , 1}
	s := &Student{"KK" , 2}

	WhoSayhi(t)
	WhoSayhi(s)
}

解释下上面的代码

  • 我们首先定义了一个接口 Human
  • 接着我们实现了两个结构体 Student 和 Teacher
  • 我们给两个结构体 Student 和 Teacher 各实现了一个抽象方法
  • 接着实现一个普通函数 函数的参数就是Human 函数内部让对象调用sayhi
  • 最后我们分别将student和teacher定义的对象传入其中 我们会惊喜的发现 我们通过结构实现了多态

接口组合

接口嵌入

如果说interface1作为interface2的一个嵌入字段 那么在interface2中就隐式的包含了interface1里面的所有函数

接口转换

超集接口可以转化为子集接口 而子集接口不能转化为超集接

就拿上面的interface1和interface2举例 in1是子集 in2是超集

所以in2能转化为in1 反之则不行

空接口

空接口(interface{})不包含任何的方法 正因为如此 所有的类型都实现了空接口 因此 空接口可以存储任意类型的数值 它有点类似于C语言的void* 类型

当函数中可以接受任意对象实例的时候 我们会将其声明为interface{} 最典型的例子就是标准库中的Print系列函数

func Println(args ...interface{})

类型查询

我们知道interface类型的变量可以存储任意类型的数值 那么我们怎么反向知道存储的是什么类型呢? 目前常用的有两种方法

  • comma-ok 断言
  • switch 测试
comma-ok 断言

Go语言中有一个语法 可以直接判断是否是该类型的变量

代码表示如下

value, ok = element.(T)

标识符含义如下

  • value : 变量的值
  • ok : bool类型的值
  • element : interface变量
  • T : 断言的类型

如果element中确实存储了T类型的变量 那么ok返回true 否则返回false

switch测试

switch测试没有什么好讲解的 就是我们拿到value之后一个个测类型即可

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

带你精通chrony服务器

华子目录 为什么会出现Chrony?Linux的两个时钟NTP介绍Chrony介绍安装与配置安装Chrony配置文件分析实验1实验2chronyc命令查看时间服务器chronyc sources输出分析其他命令 常见时区 为什么会出现Chrony? 由于IT系统中,准确的计时非常重要&am…

迭代新品 | 第四代可燃气体监测仪,守护燃气管网安全快人一步

城市地下市政基础设施是城市有序运行的生命线,事关城市安全、健康运行和高质量发展。近年来,我国燃气事故多发、频发。2020、2021、2022 年分别发生燃气事故668、1140 起、802 起,造成92、106、66 人死亡,560、763、487 人受伤。尤…

「C++」map和set的使用介绍

💻文章目录 📄前言前置知识关联式容器键值对map和set的底层结构 setset的构造函数set 的修改操作set的使用 mapmap的函数map的使用 multiset 和 multimap📓总结 📄前言 stl容器分为两类,分别是序列容器和关联式容器&am…

Java 高等院校分析与推荐系统

1)项目简介 随着我国高等教育的大众化,高校毕业生就业碰到了前所未有的压力,高校学生就业问题开始进入相关研究者们的视野。在高校学生供给忽然急剧增加的同时,我国高校大学生的就业机制也在发生着深刻的变化,作为就业…

操作系统:进程(一)

进程的基本概念 一般的解释是:进程是程序的一个执行实例,是正在执行的程序。我们写的程序编译后是一段二进制的文件。启动的时候加载到系统里面执行,就是以进程的形式执行。也就是说,我们编译后的可执行程序是一个静态的概念&…

C++ STL之string初始

我最近开了几个专栏,诚信互三! > |||《算法专栏》::刷题教程来自网站《代码随想录》。||| > |||《C专栏》::记录我学习C的经历,看完你一定会有收获。||| > |||《Linux专栏》&#xff1…

JSP基本表单和Request对象使用例子

表单的jsp&#xff1b; <%page contentType"text/html;charsetgbk" pageEncoding"UTF-8"%> <!DOCTYPE html> <html><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><titl…

golang学习笔记——接口interfaces

文章目录 Go 语言接口例子空接口空接口的定义空接口的应用空接口作为函数的参数空接口作为map的值 类型断言接口值 类型断言例子001类型断言例子002 Go 语言接口 接口&#xff08;interface&#xff09;定义了一个对象的行为规范&#xff0c;只定义规范不实现&#xff0c;由具…

数据库大事记

数据库分类分类方法为&#xff1a;按数据模型分类、按业务类型分类、按部署方式分类、按存储介质分类。 按数据模型分类 按业务类型分类 按部署方式分类 按存储介质分类 喜欢点赞收藏&#xff0c;下期再见。

【Redux】Redux 基本使用

1. Redux 快速上手 Redux 是 React 最常用的集中状态管理工具&#xff0c;类似于Vue中的Pinia&#xff08;Vuex&#xff09;&#xff0c;可以独立于框架运行。 <button id"decrement">-</button> <span id"count">0</span> <…

多线程Thread(初阶一:认识线程)

目录 一、引用线程的原因 二、线程的概念 三、进程和线程的区别 四、多线程编程 一、引用线程的原因 多任务操作系统&#xff0c;希望系统能同时运行多个任务。所以会涉及到进程&#xff0c;需要对进程进行管理、调度等。 而单任务操作系统&#xff0c;就完全不涉及到进程…

YOLOv8-Seg改进策略:全新的聚焦式线性注意力模块Focused Linear Attention | ICCV2023

🚀🚀🚀本文改进:深入分析了现有线性注意力方法的缺陷,并提出了一个全新的聚焦的线性注意力模块(Focused Linear Attention),同时具有高效性和很强的模型表达能力。 🚀🚀🚀YOLOv8-seg创新专栏:http://t.csdnimg.cn/KLSdv 学姐带你学习YOLOv8,从入门到创新,…

深度学习系列53:mmdetection上手

1. 安装 使用openmim安装&#xff1a; pip install -U openmim mim install "mmengine>0.7.0" mim install "mmcv>2.0.0rc4"2. 测试案例 下载代码和模型&#xff1a; git clone https://github.com/open-mmlab/mmdetection.git mkdir ./checkpoi…

2023年【熔化焊接与热切割】考试试卷及熔化焊接与热切割试题及解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 熔化焊接与热切割考试试卷考前必练&#xff01;安全生产模拟考试一点通每个月更新熔化焊接与热切割试题及解析题目及答案&#xff01;多做几遍&#xff0c;其实通过熔化焊接与热切割模拟考试很简单。 1、【单选题】 对…

React+后端实现导出Excle表格的功能

最近在做一个基于Reactantd前端框架的Excel导出功能&#xff0c;我主要在后端做了处理&#xff0c;这个功能完成后&#xff0c;便总结成一篇技术分享文章&#xff0c;感兴趣的小伙伴可以参考该分享来做导出excle表格功能&#xff0c;以下步骤同样适用于vue框架&#xff0c;或者…

“轻松实现文件夹批量重命名:使用顺序编号批量改名“

你是否曾经遇到过需要批量重命名文件夹&#xff0c;却因为繁琐的手动操作而感到困扰&#xff1f;现在&#xff0c;我们为你带来了一款全新的工具——轻松实现文件夹批量重命名&#xff0c;使用顺序编号批量改名。这款工具将帮助你轻松解决文件夹重命名的问题&#xff0c;提高工…

SpringSecurity5|12.实现RememberMe 及 实现原理分析

security/day08 这个功能大家还熟悉么&#xff1f;我们在登录网站的时候&#xff0c;除了让你输入用户名和密码&#xff0c;还会有个勾选框&#xff1a; 记住我&#xff01;&#xff01;&#xff01;不是让大家记住我哈。 值得一提的是&#xff0c;Spring Security 也提供了这个…

2023年汉字小达人市级比赛在线模拟题更新:40分钟150题完整对标

今天是2023年11月19日&#xff0c;距离11月30日的汉字小达人市级比赛还有11天。许多孩子正在利用难得的周末抓紧练习和备赛。 结合一些孩子的反馈和需求&#xff0c;我把150题的在线模拟题做了更新&#xff0c;增加了前面的个人信息填写的部分&#xff0c;并且把整个试卷的完成…

python自动化标注工具+自定义目标P图替换+深度学习大模型(代码+教程+告别手动标注)

省流建议 本文针对以下需求&#xff1a; 想自动化标注一些目标不再想使用yolo想在目标检测/语意分割有所建树计算机视觉项目想玩一玩大模型了解自动化工具了解最前沿模型自定义目标P图替换… 确定好需求&#xff0c;那么我们发车&#xff01; 实现功能与结果 该模型将首先…

python——第九天

今日目标&#xff1a; 偏函数 递归 字符串对象 切片 常见排序和查找 偏函数&#xff1a; python中存在一种函数的特殊使用&#xff0c;称为偏函数 如果在调用某个函数时&#xff0c;恰好某一个或者&#xff0c;某一些参数都是一个固定值&#xff08;正好不是默认值&#xff09;…