Go基础篇:类型系统

news2024/11/28 10:34:05

目录

  • 前言✨
  • 一、什么是类型?
  • 二、类型特性
    • 1、静态类型检查
    • 2、类型推断
  • 三、类型别名和自定义类型
    • 1、类型别名
    • 2、自定义类型
    • 3、类型别名和自定义类型的区别
  • 四、类型底层结构
    • 1、类型元数据
    • 2、其他描述信息
    • 3、uncommontype
  • 五、小结

前言✨

前段时间忙着春招面试,现在也算告一段落,找到一家比较心仪的公司实习,开始慢慢回归状态,这后面几章我会学习go1.19版本的语言特性或者机制:类型系统、接口、断言以及反射的内容,也算是补上之前没有深入底层的内容。

一、什么是类型?

类型的概念在不同的编程语言之间是不同的,可以用许多不同的方式来表达,但都有一些相同点。

  1. 类型是用来定义变量、常量、函数参数、函数返回值等值的属性;
  2. 在定义的变量上可以执行一组操作,例如:int 类型可以执行 + 和 - 等运算,而对于 string 类型,可以执行连接、空检查等操作;

在Go语言中,类型是用来描述变量、常量、函数参数、函数返回值等值的属性。它定义了变量或表达式可以存储的数据类型,以及可以对其执行的操作。

Go语言中的类型可以分为基本数据类型和引用类型两种。基本数据类型包括整型、浮点型、布尔型、字符串型等,而引用类型包括数组、切片、结构体、接口、channel等。

类型说明例子
布尔型表示真或假的值true, false
整型表示整数的值,有不同的位数和符号int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64
浮点型表示小数的值,有不同的精度float32, float64
复数型表示复数的值,由两个浮点数表示实部和虚部complex64, complex128
字符串型表示文本的值,使用UTF-8编码string
数组型表示固定长度的元素序列,元素类型相同[3]int, [5]string
切片型表示可变长度的元素序列,元素类型相同[]int, []string
映射型表示键值对的集合,键类型和值类型可以不同map[string]int, map[int]bool
结构体型表示一组有名字的字段,字段类型可以不同struct {name string; age int}
接口型表示一组方法的集合,用于实现多态和抽象interface {Read() error; Write() error}
函数型表示一段可执行的代码,可以有参数和返回值func(int) int, func(string) (string, error)
通道型表示用于在不同协程之间传递数据的管道,数据类型相同chan int, chan string

这些都是 Go 语言的内置类型,给内置类型和接口定义方法是错误的,哪怕用类型别名也一样。

二、类型特性

1、静态类型检查

Go语言是一种静态类型的编程语言,这意味着每个变量都有一个明确的类型,不能随意改变。
类型具有静态类型检查的特性,这意味着在编译时就能够检查出类型错误,避免了在运行时出现类型不匹配的错误。这种特性可以提高程序的可靠性和稳定性,减少调试和修复错误的时间和成本。

例如,如果一个函数需要接收一个整型参数,但是在调用该函数时传入了一个字符串类型的参数。

func IntToString(n int) {
	fmt.Println("IntToString")
}

func main() {
	var n string
	IntToString(n)
}

我们编译一下:

➜ interfaceTest (main)go build ./main.go
# command-line-arguments
./main.go:13:14: cannot use n (variable of type string) as type int in argument to IntToString

编译器就会在编译时发现这个错误,并提示开发者进行修改。这样就可以避免在运行时出现类型不匹配的错误,提高了程序的可靠性。

2、类型推断

类型推断可以根据变量的值自动推断出其类型,简化了代码的书写。这种特性可以让开发者在不显式指定变量类型的情况下,编写更加简洁、易读的代码。

例如,可以使用以下代码声明一个整型变量:

var x int = 10 // 显式地声明x为int类型,并赋值为10
y := 20 // 语法糖,隐式地声明y为int类型,并赋值为20

在这个例子中,变量 y 的类型会被自动推断为整型。这样就可以避免在声明变量时重复书写类型信息,提高了代码的可读性和简洁性。

三、类型别名和自定义类型

1、类型别名

类型别名是在Go 1.9版本中引入的一个特性,它可以让你为一个已有的类型定义一个新的名称,但是这个新的名称并不是一个新的类型,它只是一个别名,它和原来的类型是完全相同的,可以互换使用。

你可以使用type xxx = type关键字来定义一个类型别名,例如:

type MyInt = int // 定义一个int类型的别名MyInt

// 比如byte和rune就是类型别名,他们的定义如下:
type byte = uint8
type rune = int32

2、自定义类型

在Go语言中,自定义类型是在Go语言中使用type关键字来定义一个新的类型,它可以基于已有的类型或者引用类型来定义,但是它和原来的类型是不同的,它有自己的方法集合和行为。

可以使用type关键字来定义自定义类型。自定义类型可以是基本类型的别名,也可以是引用类型的新类型。

定义自定义类型的语法如下:

type TypeName BaseType // TypeName是自定义类型的名称,BaseType是基本类型或引用类型。

例如,可以使用以下代码定义一个自定义类型MyInt,它是int类型的别名:

type MyInt int

在这个例子中,MyInt类型和int类型具有相同的底层类型,但是它们是不同的类型。可以使用MyInt类型来声明变量、函数参数、函数返回值等。

除了基本类型的别名,还可以使用自定义类型来定义新的引用类型。例如,可以使用以下代码定义一个自定义类型Person,它是一个结构体类型:

type Person struct {
    Name string
    Age int
}

在这个例子中,Person类型是一个由Name和Age两个字段组成的结构体类型。可以使用Person类型来声明变量、函数参数、函数返回值等。

3、类型别名和自定义类型的区别

类型别名与自定义类型表面上看只有一个等号的差异,但他们的使用场景却完全不一样,我们通过下面的这段代码来理解它们之间的区别:

type test struct {
}

// 类型别名
type MyTest1 = test
type MyTest2 = MyTest1

// 自定义类型
type MyTest3 test
type MyTest4 MyTest3

func (test) Print(str string) {
	fmt.Println("======", str)
}

func (MyTest1) Print1(str string) {
	fmt.Println("-----", str)
}

func (MyTest2) Print2(str string) {
	fmt.Println("+++++", str)
}

func (MyTest3) Print3(str string) {
	fmt.Println("!!!!!", str)
}

func (MyTest4) Print4(str string) {
	fmt.Println("、、、、、", str)
}

func TestTypeSystem(t *testing.T) {
	var m1 MyTest1
	fmt.Printf("type of MyTest1:%T\n", m1)
	m1.Print("MyTest1")
	m1.Print1("MyTest1")
	m1.Print2("MyTest1")

	var m2 MyTest2
	fmt.Printf("type of MyTest2:%T\n", m2)
	m2.Print("MyTest2")
	m2.Print1("MyTest2")
	m2.Print2("MyTest2")

	var m3 MyTest3
	fmt.Printf("type of MyTest3:%T\n", m3)
	// m3.Print undefined (type MyTest3 has no field or method Print)
	//m3.Print("MyTest3")
	m3.Print3("MyTest3")
	// m3.Print4 undefined (type MyTest3 has no field or method Print4)
	//m3.Print4("MyTest3")

	var m4 MyTest4
	fmt.Printf("type of MyTest4:%T\n", m4)
	// m4.Print undefined (type MyTest4 has no field or method Print)
	//m4.Print("MyTest4")
	// m4.Print3 undefined (type MyTest4 has no field or method Print3)
	//m4.Print3("MyTest4")
	m4.Print4("MyTest4")
}

测试运行一下
在这里插入图片描述

结果显示 m1 和 m2 的类型是 typeSystemTest.test,表示 typeSystemTest 包下定义的 test 类型。m3 的类型是 MyTest3。m4 的类型是 MyTest4。
MyTest1 和 MyTest2 类型只会在代码中存在,编译完成时并不会有MyTest1 和 MyTest2类型,他们底层都是类型。
我们可以看出他们之间最大的区别:

  • 类型别名和原来的类型是完全相同的,可以互换使用。
  • 自定义类型是一个新的类型,它和原来的类型是不同的,它有自己的方法集合和行为。

四、类型底层结构

1、类型元数据

在Go语言中无论是内置类型还是自定义类型他们都有类型描述,也就是类型元数据,每种类型元数据都是全局唯一的,_type结构体包含了类型的名称、大小、对齐方式、哈希函数、比较函数等信息。

在runtime._type 中定义了类型元数据的结构体:

type _type struct {
	size       uintptr
	ptrdata    uintptr // size of memory prefix holding all pointers
	hash       uint32
	tflag      tflag
	align      uint8
	fieldAlign uint8
	kind       uint8
	// function for comparing objects of this type
	// (ptr to object A, ptr to object B) -> ==?
	equal func(unsafe.Pointer, unsafe.Pointer) bool
	// gcdata stores the GC type data for the garbage collector.
	// If the KindGCProg bit is set in kind, gcdata is a GC program.
	// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
	gcdata    *byte
	str       nameOff
	ptrToThis typeOff
}

_type结构体中的字段含义如下:

  • size:类型大小,单位是字节;
  • ptrdata:指针数据的大小,也就是类型中所有指针类型字段占用内存的大小;
  • hash:类型的哈希值,用于运行时的快速比较;
  • tflag:类型标志,记录了一些元信息,比如类型是否包含不导出的字段;
  • align:类型的对齐方式,也就是类型在内存中的对齐倍数。看这篇文章《Go高性能编程-了解内存对齐以及Go中的类型如何对齐保证》深入了解这个字段的意义;
  • fieldalign:类型作为结构体字段时的对齐方式;
  • kind:类型的种类,也就是基本类型还是复合类型等;
  • alg:类型算法表,包含了哈希函数和比较函数等;
  • gcdata:垃圾回收数据,用于标记类型中哪些部分是指针;
  • str:类型名称在字符串表中的偏移量;
  • ptrToThis:指向该类型指针的类型在类型表中的偏移量。

2、其他描述信息

基本数据类型只需要一个 _type 类型描述结构体就足够,但是引用类型在自身_type 存储之后,还需要额外描述信息来补充。

比如 slicetype 类型元数据结构体:

type slicetype struct {
	typ  _type
	elem *_type
}

在 slice 类型元数据后面还记录了一个 elem 的类型元数据,指向其存储元素的类型元数据。如果是 string 类型的 slice,那 elem 指针指向的就是 string 类型的元数据。

在这里插入图片描述

3、uncommontype

在Go语言中,自定义类型的类型元数据可以通过reflect包中的TypeOf函数获取。对于非常规类型(如struct、interface、chan等),其类型元数据可能包含在uncommontype结构体中。

uncommontype结构体中包含了一些非常规类型的元数据信息。

type uncommontype struct {
	pkgpath nameOff
	mcount  uint16 // number of methods
	xcount  uint16 // number of exported methods
	moff    uint32 // offset from this uncommontype to [mcount]method
	_       uint32 // unused
}

uncommontype结构体中的字段含义如下:

  • pkgpath:包路径,记录了类型所在的包;
  • mcount:方法数量,记录了类型关联的所有方法的个数;
  • xcount:导出方法数量,记录了类型关联的可导出方法的个数;
  • moff:方法偏移量,记录了从该uncommontype结构体到方法元数据数组的偏移量;
  • _:未使用,占位符。

uncommontype结构体的作用是描述类型的元数据,比如包路径和方法信息。它只有在类型有以下情况之一时才会存在:

  • 类型关联了至少一个方法;
  • 类型是一个非导出类型(首字母小写);
  • 类型是一个反射类型(实现了reflect.Type接口)。

uncommontype结构体通常紧跟在_type结构体后面,通过_type结构体中的tflag字段可以判断是否存在uncommontype结构体。

五、小结

我们知道了 uncommontype结构体定义了方法的个数和uncommontype结构体到方法元数据数组的偏移量。现在通过实例了解类型系统以及如何找到他的方法数组。

method结构体的作用是描述类型关联的方法的元数据,比如方法名和方法签名。它是一个数组元素,由uncommontype结构体中的moff字段指向:

type method struct {
	name nameOff
	mtyp typeOff
	ifn  textOff
	tfn  textOff
}

method结构体中的字段含义如下:

  • name:方法名,记录了方法的名称;
  • mtyp:方法类型,记录了方法的类型信息,比如参数和返回值;
  • ifn:接口函数,记录了方法在接口中的实现函数的地址;
  • tfn:类型函数,记录了方法在类型中的实现函数的地址。

例如,我们基于 []string 定义一个新类型 MySlice,并定义两个方法:

type MySlice []string

func (ms MySlice) Len() {
	fmt.Println(len(ms))
}

func (ms MySlice) Cap() {
	fmt.Println(cap(ms))
}

func TestType(t *testing.T) {
	var ms MySlice
	// cannot use []string{…} (value of type []string) as type string in argument to append
	// ms = append(ms, []string{"aaaa", "bbbb", "cccc"})
	ms = append(ms, MySlice{"aaaa", "bbbb", "cccc"}...)
	ms.Len()
	ms.Cap()
	fmt.Println(ms)
}

在这里插入图片描述

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

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

相关文章

JDK1.8下载、安装和环境配置教程

🎉🎉🎉点进来你就是我的人了博主主页:🙈🙈🙈戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔🦾🦾​​​​​​​ 目录 window系统安装java 下载JDK 配置环境变量 …

算法记录 | Day45 动态规划

70.爬楼梯 (进阶) 改为:一步一个台阶,两个台阶,三个台阶,…,直到 m个台阶。问有多少种不同的方法可以爬到楼顶呢? 1阶,2阶,… m阶就是物品,楼顶…

文件的使用

文章目录 1.概念1.1定义:1.2分类1.3文件名 2.文件的使用2.1文件指针2.2开闭函数2.3顺序读写2.3.1何为读写2.3.2读写函数1.字符输出fputc(输出到文件 写到文件中)2.字符输入fgetc(输入到程序 读到程序中)3.文本行函数4.…

别争了,Excel和Access都靠边,WPS也退,划时代电子表格早已出现

电子表格,绝不仅仅只是你看到的样子! 无论是Excel,还是与它抗衡30多年,不分上下的金山WPS,都没能开创出真正划时代意义的电子表格。 揭秘前让我们先简单回顾下电子表格的前世今生,等不及的可直接进度条下拉…

超写实虚拟人制作教程

最近突然吹起一阵虚拟人直播风潮,大概就是找一个虚拟人物,用主播的面部动作来驱动虚拟人来完成头部和表情动作,但我看大部分都是下载的UE5Unreal Eigine 5(Epic公司出品的一款强大的3D创作平台,很多大型3A大作都是用其…

google.common的guava依赖的partition分割产生的浅拷贝问题解决方案

google.common的guava依赖的partition分割产生的浅拷贝问题解决方案 问题背景解决方案Lyric&#xff1a; 说你不爱我 问题背景 使用google.common的guava依赖的partition分割产生的浅拷贝问题&#xff0c;如: 把userList集合分割成每个10000的小集合 List<List> partiti…

vue+gin—— GetcharZp

vuegin—— GetcharZp vuegin—— GetcharZpP1-项目介绍【03:51】P2-项目梳理【29:59】problemusercategorysubmit P3-环境搭建【05:27】P4-整合GIN、GORM【16:20】P5-整合 Swagger【10:01】P6-接口-问题列表【19:34】P7-接口-问题详情【24:06】P8-接口-用户详情【05:36】P9-接…

C++菱形继承(再剖析)

当子类对象给父类对象的时候&#xff0c;怎么找公共的虚基类&#xff08;A&#xff09; 就得通过偏移量来算虚基类的位置 ---------------------------------------------------------------------------------------------------------------------------- 我们来分析一下B…

DBi-Tech Studio Controls 1.6 for .NET Crack

Studio Controls for .NET 为 Windows 开发人员提供了 18 个 .NET 控件&#xff0c;用于 Outlook 风格的调度到管理单元报告、Windows 应用程序导航到现代 UI 设计演示以实现自然的最终用户功能。 Appointment Scheduling、Time Tabling、Day Timer ... 应…

【Unity入门】20.三维向量

【Unity入门】三维向量 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity入门系列博客&#xff0c;所学知识来自B站阿发老师~感谢 &#xff08;一&#xff09;空间向量 &#xff08;1&#xff09;什么是三维向量 为什么会有这么一篇博客呢&#xff1f;主要是三维向量在unity中…

[Redis] 数据结构zset压缩列表实现和跳表实现讲解

&#x1f61a;一个不甘平凡的普通人&#xff0c;致力于为Golang社区和算法学习做出贡献&#xff0c;期待您的关注和认可&#xff0c;陪您一起学习打卡&#xff01;&#xff01;&#xff01;&#x1f618;&#x1f618;&#x1f618; &#x1f917;专栏&#xff1a;算法学习 &am…

多级缓存建设方案

项目背景 xx系统中对容量和耗时有较高要求&#xff0c;以支付优惠立减为例&#xff0c;每个用户咨询可用立减时&#xff0c;都会过一遍全量生效活动。目前日常活动数3000&#xff0c;目标2w&#xff1b;日常秒级咨询量1w&#xff0c;大促22w。所以如何支撑日常和大促的业务非常…

Unity利用UGUI RawImage组件制作转场动画

Unity利用UGUI RawImage组件制作转场动画 最近接到了一个unity全景图的小项目&#xff0c;由于在不同的场景之间转场时直接转会太过生硬&#xff0c;因此要求有个Alpha转场的动画。于是想到两种可行的方案&#xff1a; 一、UGUI方案 用UGUI显示当前屏幕纹理&#xff0c;然后…

python处理图像的各种技术镜像、旋转、遮挡、叠加、条带化

2.6 图像镜面对称 1、将图像水平镜面转换。 2、将图像垂直镜面转换。 import random #导入模块 import numpy as np import matplotlib.pyplot as plt a plt.imread("1.jpg") # 将图像沿着水平方向重复三次。 ba.copy() da.copy() # 将图像水平镜面转换。&…

【笔记】cuda大师班1-4

一.基本概念 进程&#xff08;process&#xff09;一个正在被执行的计算机程序的实例 上下文&#xff08;context&#xff09;&#xff1a;待处理数据的集合&#xff0c;允许处理器暂停&#xff0c;保持处理的执行和恢复处理 并发&#xff1a;上下文切换&#xff0c;主要应用于…

AIGC - 生产力新工具 Copilot

文章目录 介绍能干啥IDEA 安装 介绍 https://github.com/features/copilot 能干啥 Copilot的主要功能包括: 代码补全和提示:Copilot会根据上下文,智能提示您可能需要的变量,函数,参数等。快速生成代码:Copilot可以快速生成if语句,for循环,类定义,函数定义等代码模板。代码优…

Unity之OpenXR+XR Interaction Toolkit实现 传送

前言 VR中由于走动比较容易头晕&#xff0c;所以基本上玩家移动都是靠传送&#xff0c;这样用户更加直观&#xff0c;传送过去也不会感觉头晕。 好了&#xff0c;那么我们一起来说实现一下OpenXR的传送功能。 准备 我们新建一个3D&#xff08;URL&#xff09;项目&#xff0…

git 撤销add/commit,以及更换源命令

前言&#xff1a;主要是为了自己方便记录&#xff0c;省的每次都查找一下这些命令 1、当我们只是想撤回commit&#xff0c;保留add .的时候&#xff0c;可以用下方代码 git reset --soft HEAD^ 2、当我们想撤回commit以及add .的时候&#xff0c;可以用下方代码 git reset…

Mesh形变算法

前言&#xff1a; 作者正好因为动画、模拟仿真等等的重大需求需要预先研发离散形的模型Mesh的形变算法&#xff0c;并且要验证、研究适用的范围、特别是性能等等&#xff0c;摸着石头过河别喷&#xff0c;毕竟我主要是渲染、动画、引擎的对于计算几何、三维重建不是很熟悉&…

Python小姿势 - Python使用Jupyter Notebook

Python使用Jupyter Notebook Jupyter Notebook是一个开源的Web应用程序&#xff0c;可以用来创建和共享包含 live code&#xff0c;公式&#xff0c;可视化和解释性文本的文档。 安装Jupyter Notebook 首先&#xff0c;确保你安装了正确的Python版本和包管理器&#xff08;pip&…