【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 stringid int address string
} 

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

其他匿名字段

非结构体类型和指针类型

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

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

方法 – 封装

概述

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

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

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

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


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

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

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

为类型添加方法

基础类型作为接受者

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

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

package mainimport "fmt"type Myint int// 在函数定义嵌 在其名字之间放上一个变量 即是一个方法
func (a Myint) Add(b Myint) (result Myint) {return a + b
}func main() {var a Myint = 1fmt.Println("a.Add(2) = ", a.Add(2)) // 答案是3
}

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

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

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

结构体作为接受者

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

type Person struct {name stringsex  byteage  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 stringsex  byteage  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 stringid   int
}type Teacher struct {name stringid   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.mzph.cn/news/149355.shtml

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

相关文章

【HarmonyOS】鸿蒙应用开发基础认证题目

系列文章目录 【HarmonyOS】鸿蒙应用开发基础认证题目; 文章目录 系列文章目录前言一、判断题二、单选题三、多选题总结 前言 随着鸿蒙系统的不断发展,前不久,华为宣布了重磅消息,HarmonyOS next 开发者版本会在明年(…

sqli-labs关卡20(基于http头部报错盲注)通关思路

文章目录 前言一、回顾上一关知识点二、靶场第二十关通关思路1、判断注入点2、爆数据库名3、爆数据库表4、爆数据库列5、爆数据库关键信息 总结 前言 此文章只用于学习和反思巩固sql注入知识,禁止用于做非法攻击。注意靶场是可以练习的平台,不能随意去尚…

SELinux零知识学习二十、SELinux策略语言之类型强制(5)

接前一篇文章:SELinux零知识学习十九、SELinux策略语言之类型强制(4) 二、SELinux策略语言之类型强制 3. 访问向量规则 AV规则就是按照对客体类别的访问许可指定具体含义的规则,SELinux策略语言目前支持四类AV规则: …

centos7中安装Nginx和使用Nginx详细操作

环境: 准备了三台centos7虚拟机:192.168.213.4、192.168.213.5、192.168.213.6。 一、安装 三台虚拟机都安装下面的步骤执行,安装Nginx,为后面的使用演示使用。 1、安装必备组件: sudo yum install yum-utils2、配置yum源 在下面的文件目录…

Linux系统下安装RabbitMQ超简单教程(非详细)(Centos8)

文章目录 一、下载所需安装包二、安装三、启动rabbitmq四、添加远程用户五、图形化访问六、修改rabbitmq的启动端口和管理端口(没有这个需求就不用看了)七、需要注意版本问题可能遇到的错误和解决方式version GLIBC_2.34 类型错误undefined function rab…

云原生微服务-理论篇

文章目录 分布式应用的需求分布式架构治理模式演进ESB 是什么?微服务架构 MSA微服务实践细节微服务治理框架sidercar 什么是service mesh?康威定律微服务的扩展性什么是MSA 架构?中台战略和微服务微服务总体架构组件微服务网关服务发现与路由…

硬盘无法格式化怎么办?

许多用户在尝试格式化硬盘、SD卡、USB闪存驱动器时可能会遇到无法格式化硬盘的问题,并且还会伴随着Windows无法完成格式化或格式化未成功完成之类的错误消息弹窗。那么,硬盘无法格式化原因是什么呢?硬盘无法格式化怎么办呢?下面我…

MIB 6.1810实验Xv6 and Unix utilities(5)find

难度:moderate Write a simple version of the UNIX find program for xv6: find all the files in a directory tree with a specific name. Your solution should be in the file user/find.c. 题目要求:实现find ,即在某个路径中,找出某…

解决 uniapp 开发微信小程序 不能使用本地图片作为背景图 问题

参考博文:uniapp微信小程序无法使用本地静态资源图片(背景图在真机不显示)的解决方法_javascript技巧_脚本之家 问题:uniapp 开发微信小程序,当使用本地图片作为 background-image 时,真机无法显示 解决: 方法一&am…

计算机毕业设计选题推荐-个人博客微信小程序/安卓APP-项目实战

✨作者主页:IT毕设梦工厂✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

HTML5-原生History

更多内容,访问: history hash 单页面应用和多页面应用 React-Router源码分析-History库 History库源码分析-Action 动作类型 History库源码分析-createLocation History库源码分析-createPath History库源码分析-parsePath history 浏览器历史记录对象 属性: le…

来聊聊阿里1688 /拼多多API接口接入| 让需求回到产品端

昨儿办公室讨论起了1688。 对,就是阿里搞批发的那个网站。 在上面,你可以买到各种各样价格低廉的产品,比如,办公用具、女孩子的皮筋、小孩子的玩具等等。 在小批量上,它和拼多多定价类似,但二者的赛道却不同…

PHP 中传值与传引用的区别,什么时候传值什么时候传引用?

传值:当使用传值的方式时,函数或方法会创建原始变量的一个副本,并将该副本传递给函数或方法。在函数或方法内部,对副本的任何修改都不会影响到原始变量。当函数或方法执行完毕后,副本被销毁,不再使用。 传引…

Unity--互动组件(Toggle Group)||Unity--互动组件(Slider)

Toggle Group 属于同一组的切换开关受到限制,因此一次只能打开其中一个开关,按下其中一个开关时,其他的开关将会自动关闭; Allow Switch Off:(允许关闭) 如果禁用此设置,则按下当前…

开发《星球大战》小游戏的意义

开发《星球大战》小游戏的意义有以下几点: 学习和掌握游戏开发的基本技能:通过开发《星球大战》小游戏,可以学习和掌握游戏开发的基本技能,包括游戏策划、游戏设计、游戏编程和游戏测试等方面的技能。加深对游戏行业的了解&…

大数据HCIE成神之路之数学(2)——线性代数

线性代数 1.1 线性代数内容介绍1.1.1 线性代数介绍1.1.2 代码实现介绍 1.2 线性代数实现1.2.1 reshape运算1.2.2 转置实现1.2.3 矩阵乘法实现1.2.4 矩阵对应运算1.2.5 逆矩阵实现1.2.6 特征值与特征向量1.2.7 求行列式1.2.8 奇异值分解实现1.2.9 线性方程组求解 1.1 线性代数内…

GO 抽象工厂模式设计

既然工厂模式每个产品都需要实现对应的工厂类去生成相关实例,提取产品的共性,提高代码的内聚性, 就是抽象工厂模式要干的。在抽象工厂中,依然是不同产品对应不同的工厂类,但可以尽可能将具有相同共性的产品类别合在一起…

什么是虚拟DOM(Virtual DOM),说说工作原理

虚拟DOM(Virtual DOM)是一种将页面的状态抽象为JavaScript对象表示的概念,用于提高Web应用程序的性能和渲染效率。 虚拟DOM的工作原理如下: 1: 初始渲染:首先,通过JavaScript对象(…

Revive开发商加入VR开源标准OpenXR

导读作为一款能让HTC Vive用户玩到Oculus平台游戏的软件,它的开发商CrossVR今日宣布即将加盟为VR和AR应用程序开源组织,即OpenXR。 由Khronos Group引领的OpenXR旨在创建一个标准化且免版税的应用程序编程接口(API),该…