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 intName string
}

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

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

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

type Tes struct{id intName 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 testype person struct {id   intname stringage  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 {personsex 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 intpersonsex string
}func (this *Man) GetSuperId() int {return this.person.id
}func (this *Man) GetManId() int {return this.id
}

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

type Woman struct {person personsex    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   intname stringage  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 = 4fmt.Printf("%+v\n", p.GetIdPtr())p.id = 5fmt.Printf("%+v\n", p.GetId())
}

在这里插入图片描述

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

type person struct {id   intname stringage  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_identifiersPkgPath stringType      Type      // field typeTag       StructTag // field tag stringOffset    uintptr   // offset within struct, in bytesIndex     []int     // index sequence for Type.FieldByIndexAnonymous 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.Tagfmt.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只能操作已有的字段和函数,不能动态的增加或者减少字段和函数。
除了使用第三方库,借助上述语法,自己也可以定义需要的操作比如判空。

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

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

相关文章

最简单的基于 FFmpeg 的收流器(以接收 RTMP 为例)

最简单的基于 FFmpeg 的收流器(以接收 RTMP 为例) 最简单的基于 FFmpeg 的收流器(以接收 RTMP 为例)正文结果工程文件下载参考链接 最简单的基于 FFmpeg 的收流器(以接收 RTMP 为例) 参考雷霄骅博士的文章…

蓝凌OA frpt_listreport_definefield.aspx接口存在SQL注入漏洞

免责声明:文章来源互联网收集整理,请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该…

DevStack 部署 OpenStack

Devstack 简介 DevStack 是一系列可扩展的脚本,用于基于 git master 的最新版本快速调出完整的 OpenStack 环境。devstack 以交互方式用作开发环境和 OpenStack 项目大部分功能测试的基础。 devstack 透过执行 stack.sh 脚本,搭建 openstack 环境&…

lv20 QT主窗口4

熟悉创建主窗口项目 1 QAction 2 主窗口 菜单栏:fileMenu menuBar()->addMenu(tr("&File")); 工具栏:fileToolBar addToolBar(tr("File")); 浮动窗:QDockWidget *dockWidget new QDockWidget(tr("Dock W…

Threejs之精灵模型Sprite

参考资料 精灵模型Sprite…Sprite模拟下雨、下雪 知识点 注:基于Three.jsv0.155.0 精灵模型Sprite精灵模型标注场景(贴图)Sprite模拟下雨、下雪 精灵模型Sprite Three.js的精灵模型Sprite和Threejs的网格模型Mesh一样都是模型对象,父类都是Object3…

Matlab 最小二乘插值(曲线拟合)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 在多项式插值时,当数据点个数较多时,插值会导致多项式曲线阶数过高,带来不稳定因素。因此我们可以通过固定幂基函数的最高次数 m(m < n),来对我们要拟合的曲线进行降阶。之前的函数形式就可以变为: 二、实现…

spring Boot 报错RedisConnectionFailureException

错误描述&#xff1a; 错误重点&#xff1a;&#xff08;图片中蓝色区域&#xff09; Unable to connect to Redis; 无法连接到Redis Unable to connect to 127.0.0.1 无法连接到本地服务器 所以&#xff0c;错误是本地服务器没有连接上Redis所引起的 错误解析…

Linux——进程控制(二)进程等待

目录 前言 一、进程等待 二、如何进行进程等待 1.wait 2.waitpid 2.1第二个参数 2.2第三个参数 3. 等待多个进程 三、为什么不用全局变量获取子进程的退出信息 前言 前面我们花了大量的时间去学习进程的退出&#xff0c;退出并不难&#xff0c;但更深入的学习能为本…

048 异常

什么是异常 异常体系结构 异常的继承关系 Error Exception 异常处理机制 try&#xff1a;用{}将可能产生异常的代码包裹catch&#xff1a;与try搭配使用&#xff0c;捕获try包裹代码中抛出的异常并进行后续动作finally&#xff1a;跟在try后&#xff0c;在try和catch之后执行…

web3时事粥报

比特币正成为更具有吸引力的通胀对冲工具 在通胀的宏观经济浪潮中&#xff0c;比特币正逐渐崭露头角&#xff0c;成为那些渴望多元化投资组合的投资者眼中的璀璨明星。Kooner 预测&#xff0c;2024年&#xff0c;各种宏观经济挑战可能进一步提升比特币、黄金和白银等资产的避险…

Google Dremel和parquet的复杂嵌套数据结构表征方法解析

转载请注明出处。作者&#xff1a;archimekai 核心参考文献&#xff1a; Dremel: Interactive Analysis of Web-Scale Datasets 文章目录 引言复杂嵌套数据结构的无损表征问题Dremel论文中提出的表征方法parquet备注 引言 Dremel是Google的交互式分析系统。Google大量采用prot…

搭建服务器及跨域处理

使用内置的模块搭建服务器 自己电脑: 域名:localhost ip:127.0.0.1 http模块搭建服务器 const http = require(http)// 创建一个http对应的服务器,每次改完服务器的代码后都需要重新启动下服务器 /*方式一: const server = http.createServer((request,response)=>{…

铁路关基保护新规发布!铁路软件供应链安全洞察与治理思路

近日&#xff0c;国家铁路局发布《铁路关键信息基础设施安全保护管理办法》&#xff0c;《办法》第十四条提到&#xff1a;“运营者应当加强铁路关键信息基础设施供应链安全保护&#xff0c;优先采购安全可信的网络产品和服务。运营者采购网络产品和服务&#xff0c;应当预判该…

Intel FPGA IP之LVDS SerDes IP学习

FPGA 视频数据输入输出直通工程&#xff1a; 屏&#xff1a;13.2吋8bit色深&#xff0c;屏幕分辨率为1440*192060&#xff0c;具有两个Port&#xff0c;每个Port有4个差分数据对与1个差分时钟对&#xff0c;差分对均支持LVDS协议芯片&#xff1a;Cyclone V系列FPGA目的&#x…

Noise Conditional Score Networks(NCSN)学习

参考&#xff1a; [1] https://zhuanlan.zhihu.com/p/597490389 [2] https://www.zhangzhenhu.com/aigc/Score-Based_Generative_Models.html TOC 1 基于分数的生成模型1.1 简介和动机1.2 Score Matching及其改进1.2.1 Score Matching1.2.2 Sliced score matching&#xff08;不…

XSS_lab(level1-level5)

level1 直接输入页面没有发现输入框&#xff0c;观察url发现有传参 尝试修改传参为&#xff1a;<script>alert(1)</script> 过啦&#xff01; level2 页面中有输入框&#xff0c;尝试构建语句&#xff1a;<script>alert(1)</script>,传输后查看源代…

国际心理学导师-叶子文JeffreyYip的《意识地图》

“物质就是能量。” ---爱因斯坦 “時常保持觉知&#xff0c;有意识地发现情绪起伏 你随时都能翻转人生 做自己人生的导演 当你频率高时&#xff0c;万事万物为你而来” ---大卫霍金斯 叶子文-《意识地图》&#xff1a;高阶心理学课程 宇宙间万物的本质是能量。一切都靠能量…

Java基础---lambda表达式

一、为什么要引入lambda表达式 lambda 表达式是一个可传递的代码块 &#xff0c; 可以在以后执行一次或多次 。 在介绍lambda表达式之前&#xff0c;我们看一下&#xff0c;以前&#xff0c;我们对于一个问题的通常写法。 假设你已经了解了如何按指定时间间隔完成工作&#xf…

js字符串转json的3种方法

1.eval方式解析 function strToJson(str){var json eval("(" str ")");return json;}console.log(strToJson("{int:1, string:demo}")); 运行截图&#xff1a; 注&#xff1a; 记得别忘了str两旁的小括号。 永远不要使用 eval !!! eval() 是一…

611. 有效三角形的个数 - 力扣

1. 题目 给定一个包含非负整数的数组 nums &#xff0c;返回其中可以组成三角形三条边的三元组个数。 2. 示例 3. 分析 利用已升序了的数组通过 a b > c 这条公式找出符合要求的三元组&#xff0c;利用这个公式的前提是三条边为从小到大&#xff0c;再利用单调性快速统计…