Go-知识struct

Go-知识struct

  • 1. struct 的定义
    • 1.1 定义字段
    • 1.2 定义方法
  • 2. struct的复用
  • 3. 方法受体
  • 4. 字段标签
    • 4.1 Tag是Struct的一部分
    • 4.2 Tag 的约定
    • 4.3 Tag 的获取

githupio地址:https://a18792721831.github.io/

1. struct 的定义

Go 语言的struct与Java中的class类似,可以定义字段和方法,但是不能继承。

1.1 定义字段

struct定义字段非常简单,只需要将字段写在struct的结构中,比如:

type tes struct {
	a int
	Name string
}

需要注意的是,在Go里面,访问权限是通过name的大小写指定的,小写表示包内可见,如果是大写则表示包外可见。
所以上面的struct如果用Java翻译:

class tes {
    private int a;
    public String Name;
} 

同样的,如果创建的struct想让包外可见,那么必须是大写开头。

type Tes struct{
    id int
    Name string
}

1.2 定义方法

在Go里面一般不会区分函数和方法,或者更好理解的话,可以认为方法是受限的函数,限制了函数调用者,那么就是方法。
定义方法:

func (a tes) test() {
    fmt.Println(a.id)
}

同样的,上述方法包内可见。

func (a tes) Test() {
    fmt.Println(a.Name)
}

上述方法虽然包外可见,但是没有意义,因为tes是包内可见,如果没有对外提供函数,那么是没有意义的。
如果想保证安全,可以使用包内可见的struct配合包内字段加包外方法,另外额外提供包外可见的struct获取函数,实现类似于Java的可见性控制。

package tes

type person struct {
	id   int
	name string
	age  int
}

func (this *person) GetId() int {
	return this.id
}

func (this *person) GetName() string {
	return this.name
}

func (this *person) GetAge() int {
	return this.age
}

func (this *person) SetId(id int) {
	this.id = id
}

func (this *person) SetName(name string) {
	this.name = name
}

func (this *person) SetAge(age int) {
	this.age = age
}

func NewPerson() *person {
	return &person{}
}

func NewPersonWithId(id int) *person {
	return &person{id: id}
}

func NewPersonWithName(name string) *person {
	return &person{name: name}
}

因为Go不支持函数重载,所以需要用不同的函数名字区分。
上述代码实际上就是一个基本的JavaBean的实现。
但是实际使用上,基本上对外可见的字段都是直接用.来访问和赋值的。
在使用上,struct是否对外可见,则和编码风格相关,业务系统一般不会考虑封闭性,基本上struct都是可见的;而第三方包等为了保证安全性,则会将部分struct设置为包内可见,在结合interface来保证扩展性。

2. struct的复用

在其他编程语言中,使用继承或组合实现代码的复用。
而Go语言中没有继承,只能使用组合实现复用。
比较特别的是,在Go语言中,组合复用的struct可以认为拷贝了被组合的struct的字段到需要的struct中。

type Man struct {
	person
	sex string
}

func (this *Man) ToString() string {
	return fmt.Sprintf("id=%d, name=%s, age=%d, sex=%s\n", this.person.id, this.person.name, this.person.age, this.sex)
}

func (this *Man) GetToString() string {
	return fmt.Sprintf("id=%d, name=%s, age=%d, sex=%s\n", this.id, this.name, this.age, this.sex)
}

在struct中组合其他struct,相当于是创建了一个同名的隐式字段,在使用的时候,可以指明隐式字段,也可以不指明隐式字段。
想一想,在Java中,如果当前class和父class中有同名的字段,那么在使用父类中的字段时,需要使用super指明使用的是父类中的字段。
同理的,当struct中有一个id,那么在使用的时候,可以使用隐式字段指明:

type Man struct {
	id int
	person
	sex string
}

func (this *Man) GetSuperId() int {
	return this.person.id
}

func (this *Man) GetManId() int {
	return this.id
}

隐式字段如果显示的定义了,那么就无法像使用自己的字段一样使用内嵌字段了:

type Woman struct {
	person person
	sex    string
}

func (this *Woman) ToString() string {
	return fmt.Sprintf("id=%d, name=%s, age=%d, sex=%s\n", this.person.id, this.person.name, this.person.age, this.sex)
}

func (this *Woman) GetString() string {
	return fmt.Sprintf("id=%d, name=%s, age=%d, sex=%s\n", this.id)
}

如果还像使用自己的字段一样使用内嵌字段,就会找不到
在这里插入图片描述

3. 方法受体

方法本质上还是函数,只是限制了函数的调用者。
那么你有没有好奇,为什么上面的例子中,方法的调用者都是指针类型而不是struct类型,这有什么区别?

type person struct {
	id   int
	name string
	age  int
}

func (this *person) SetIdPtr(id int) {
	this.id = id
}

func (this person) SetId(id int) {
	this.id = id
}

func (this *person) GetIdPtr() int {
	return this.id
}

func (this person) GetId() int {
	return this.id
}

func TestPerson(t *testing.T) {
	p := person{
		id:   1,
		name: "zhangsan",
		age:  10,
	}
	fmt.Printf("%+v\n", p)
	p.SetId(2)
	fmt.Printf("%+v\n", p)
	p.SetIdPtr(3)
	fmt.Printf("%+v\n", p)
	p.id = 4
	fmt.Printf("%+v\n", p.GetIdPtr())
	p.id = 5
	fmt.Printf("%+v\n", p.GetId())
}

在这里插入图片描述

没错,区别在于是否会影响原数据。
函数调用过程中,会将函数压入调用栈,在入栈过程中,会对函数参数进行拷贝。
在Java中,如果是基本类型参数,那么拷贝值,如果是复杂类型参数,那么拷贝指针。
在Go语言中,可以由程序员指定,如果方法调用者是指针,那么表示方法可以修改外部数据,如果方法调用者是struct,那么不会修改外部数据。
如果是数据的读取,那么不管是指针还是struct,都能读取到数据。
在换一个角度看,方法的调用者,在方法调用的时候,也进行了参数拷贝,所以可以认为方法调用者就是一个特殊的参数。

type person struct {
	id   int
	name string
	age  int
}

func (this person) GetNameS() string {
	return this.name
}

func GetName(this *person) string {
	return this.name
}

func TestPerson(t *testing.T) {
	p := person{
		id:   1,
		name: "zhangsan",
		age:  10,
	}
	fmt.Println(p.GetNameS())
	fmt.Println(GetName(&p))
}

运行都能获取到结果
在这里插入图片描述

只是无法使用.的方式触发了。

4. 字段标签

在Go语言的struct的字段后面,可以使用标签。

type person struct {
	id   int    `tagKey:"tagValue1,tageValue2"`
	name string `tagKey:"tagValue1,tageValue2"`
	age  int    `tagKey:"tagValue1,tageValue2"`
}

4.1 Tag是Struct的一部分

Tag用于标识字段的额外属性,类似注释。标准库reflect包中提供了操作Tag的方法。

// A StructField describes a single field in a struct.
type StructField struct {
	// Name is the field name.
	Name string

	// PkgPath is the package path that qualifies a lower case (unexported)
	// field name. It is empty for upper case (exported) field names.
	// See https://golang.org/ref/spec#Uniqueness_of_identifiers
	PkgPath string

	Type      Type      // field type
	Tag       StructTag // field tag string
	Offset    uintptr   // offset within struct, in bytes
	Index     []int     // index sequence for Type.FieldByIndex
	Anonymous bool      // is an embedded field
}

StructTag其实就是字符串:
在这里插入图片描述

4.2 Tag 的约定

Tag本质上是个字符串,那么任何字符串都是合法的,但是在实际使用中,有一个约定:key:"value.."格式,如果有多个,中间用空格区分。

type person struct {
	id   int    `tagKey:"tagValue1,tageValue2" tagKey1:"tagValue1,tageValue2"`
	name string `tagKey:"tagValue1,tageValue2" tagKey1:"tagValue1,tageValue2"`
	age  int    `tagKey:"tagValue1,tageValue2" tagKey1:"tagValue1,tageValue2"`
}

key: 必须是非空字符串,字符串不能包含控制字符、空格、引号、冒号。
value: 以双引号标记的字符串。
key和value之间使用冒号分割,冒号前后不能有空格。
多个key-value之间用空格分割。

key一般用于表示用途,value一般表示控制指令。
比如:
json:"name,omitempty"
表示json转换的时候,使用name作为名字,如果字段值为空,那么json转换该字段的时候忽略。

4.3 Tag 的获取

reflectStructField提供了GetLookup方法:
在这里插入图片描述

比如获取上面person的Tag

func TestPerson(t *testing.T) {
	p := person{
		id:   1,
		name: "zhangsan",
		age:  10,
	}
	st := reflect.TypeOf(p)
	stf, ok := st.FieldByName("id")
	if !ok {
		fmt.Println("not found")
		return
	}
	nameTag := stf.Tag
	fmt.Printf("tagKey=%s\n", nameTag.Get("tagKey"))
	tagValue, ok := nameTag.Lookup("tagKey1")
	if !ok {
		fmt.Println("not found")
		return
	}
	fmt.Printf("tagKey1=%s\n", tagValue)
}

在这里插入图片描述

在Java中有一个非常强大,也经常使用的插件lombok,通过在class的字段上添加注解,进而实现一些控制方法。
区别在于,lombok是在编译时,通过操作字节码,实现方法的写入,而Tag是在运行时,通过反射赋值。
所以Tag只能操作已有的字段和函数,不能动态的增加或者减少字段和函数。
除了使用第三方库,借助上述语法,自己也可以定义需要的操作比如判空。

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

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

相关文章

数据结构c版(2)——二叉树

本章我们来了解一下二叉树这一概念。 目录 1.树概念及结构 1.1树的概念​​​​​​​ 1.2 树的特点: 1.3 树的相关概念 1.4 树的表示​​​​​​​ 1.5 树在实际中的运用(表示文件系统的目录树结构) 2.二叉树概念及结构 2.1概念 …

华为云命令行工具KooCLI—高效云端管理的秘诀

做运维多年,公司从传统运维改为云上。刚一接触时,确实因为要学习很多云知识而烦恼。每次想要执行某个操作时,都要先登录到云平台,浏览界面,寻找正确的按钮。这样不仅浪费时间,还经常出错。直到有一天&#…

【深度学习笔记】计算机视觉——锚框

锚框 目标检测算法通常会在输入图像中采样大量的区域,然后判断这些区域中是否包含我们感兴趣的目标,并调整区域边界从而更准确地预测目标的真实边界框(ground-truth bounding box)。 不同的模型使用的区域采样方法可能不同。 这里…

STM32F103ZET6移植FreeRTOS

FreeRTOS是一款免费开源的轻量级操作系统 一、获取源码 方式一、官网:www.freertos.org 方式二(推荐)、托管网址: FreeRTOS Real Time Kernel (RTOS) - Browse /FreeRTOS at SourceForge.net 找到对应的版本直接下载.ZIP文件…

2023年09月CCF-GESP编程能力等级认证Scratch图形化编程三级真题解析

本文收录于专栏《Scratch等级认证CCF-GESP真题解析》,专栏总目录・点这里 一、单选题(共15题,共30分) 第1题 我国第一台大型通用电子计算机使用的逻辑部件是( )。 A:集成电路 B:大规模集成电路 C:晶体管 D:电子管 答案:D 第2题 下列流程图的输出结果是?( ) …

【自然语言处理】BitNet b1.58:1bit LLM时代

论文地址:https://arxiv.org/pdf/2402.17764.pdf 相关博客 【自然语言处理】BitNet b1.58:1bit LLM时代 【自然语言处理】【长文本处理】RMT:能处理长度超过一百万token的Transformer 【自然语言处理】【大模型】MPT模型结构源码解析(单机版)…

排序(1)——直接插入排序+冒泡排序

目录 1 排序的概念及其应用 1.1 排序的概念 1.2 排序的应用 1.3 常见的排序算法 2 直接插入排序 2.1 基本思想 2.2 基本思路 2.3 代码实现 2.4 时间复杂度 3 冒泡排序(回顾) 3.1 思路分析 3.2 时间复杂度 4 比较 1 排序的概念及其应用 1.…

STM32 (1)

1.基本信息 stm32是由ST公司生产的一种32位微控制器(单片机)。 1.1 各种型号 stm32是32位单片机的总称,有多种不同的系列。 32即用32个比特位表示一个地址,寻址范围:0x00000000 --0xffffffff (4GB) 1.2 存储密度 …

「优选算法刷题」:在每个树行中找最大值

一、题目 给定一棵二叉树的根节点 root &#xff0c;请找出该二叉树中每一层的最大值。 示例1&#xff1a; 输入: root [1,3,2,5,3,null,9] 输出: [1,3,9]示例2&#xff1a; 输入: root [1,2,3] 输出: [1,3]提示&#xff1a; 二叉树的节点个数的范围是 [0,104]-231 < N…

Flink:Temporal Table Function(时态表函数)和 Temporal Join

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…

第十四届蓝桥杯大赛B组 JAVA 蜗牛 (递归剪枝)

题目描述&#xff1a; 这天&#xff0c;一只蜗牛来到了二维坐标系的原点。 在 x 轴上长有 n 根竹竿。它们平行于 y 轴&#xff0c;底部纵坐标为 0&#xff0c;横坐标分别为 x1, x2, …, xn。竹竿的高度均为无限高&#xff0c;宽度可忽略。蜗牛想要从原点走到第 n 个竹竿的底部也…

Ubuntu20.04使用XRDP安装原生远程桌面

Ubuntu20.04使用XRDP安装原生远程桌面 1.安装gnome桌面 # 如果没有更新过源缓存&#xff0c;先更新一下 sudo apt update# 安装gnome桌面 # 可选参数 --no-install-recommends&#xff0c;不安装推荐组件&#xff0c;减少安装时间和空间占用 sudo apt install ubuntu-desktop…

Docker基础教程 - 1 Docker简介

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 1 Docker简介 Docker是一个强大的容器化平台&#xff0c;让你能够更轻松地构建、部署和运行应用程序。 下面我们来学习 Docker。 1.1 Docker是什么 1 现在遇到的问题 每次部署一台服务器&…

Apache JMeter 5.6.3 安装

源码下载 curl -O https://dlcdn.apache.org//jmeter/source/apache-jmeter-5.6.3_src.zipJMeter 下载 curl -O https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.6.3.zipjmeter.properties 里 设置中文 windows系统上解压&#xff0c;双击jmeter.bat 启动 执行参…

618快递准点到达,别忘了感谢它!

进入6月以来&#xff0c;全国快递日均业务量飞速上涨。 虽然618大促是电商的主场&#xff0c;但作为不可或缺的物流环节&#xff0c;为了这场年中大考&#xff0c;快递企业在此期间也使尽浑身解数&#xff0c;竞相比拼配送速度。那么&#xff0c;为了更快的时效&#xff0c;快递…

【基于Matlab GUI的语音降噪系统设计】

客户不要了&#xff0c;挂网上吧&#xff0c;有需要自行下载~ 赚点辛苦费 ** 功能实现: ** 1、导入音频文件/录入音频&#xff0c;能实现播放功能。 2、对导入/录入的音频信号进行时域和频域分析&#xff0c;并制图。 3、可在导入/录入的音频信号上加入噪声&#xff0c;并能够播…

零基础手把手教你创建微信小程序(十六)·事件传参·data-*自定义数据

事件传参&#xff1a;在触发事件时,将一些数据作为参数传递给事件处理函数的过程,就是事件传参。 在微信小程序中,我们经常会在组件上添加一些自定义数据,然后在事件处理函数中获取这些自定义数据,从而完成业务逻辑的开发。 在组件上通过data-"的方式定义需要传递的数据,其…

被通知回老家当农场主,没有经验的我用FarmOS系统抢先体验了一把!

网管小贾 / sysadm.cc 公司小Z过年回来就变得有点魔怔&#xff0c;工作积极性不高&#xff0c;天天话里话外总是唠叨着要辞职回老家种地&#xff01; 老板让我去劝劝他&#xff0c;强调务必对齐颗粒度&#xff0c;说劝好了给我记上一功。 我也不知道之前的那些功啥时候能变现…

【动态规划专栏】

动态规划基础知识 概念 动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09;&#xff1a;用来解决最优化问题的算法思想。 动态规划是分治思想的延伸&#xff0c;通俗一点来说就是大事化小&#xff0c;小事化无的艺术。 一般来说&#xff0c;…

OpenAI钦点的“机器人界OpenAI”来了:成立不到两年估值破26亿美元

OpenAI们正在今年因AI而再次火热无比的机器人领域“复刻”一个OpenAI。 2024年2月23日&#xff0c;OpenAI、微软、贝佐斯风投、英伟达等总计18位投资公司向一家机器人公司注资了6.75亿美元&#xff0c;这家公司就是Figure AI。 Figure AI成立于2022年&#xff0c;两年不到经过…