Go
本文旨在作者快速介入Go语言学习,故仅保留最基础的部分,存在部分知识点缺失,且相对较为粗略,还请有意阅读者谅解,可自行查阅资料补齐其余知识点,日后作者有计划对相应内容进行补充。
1. Go语言介绍
Go语言 全称Golang,由Google在2009年发布。
-
种类:静态强类型 、编译型 、并发型
-
语法:C语言高性能 (语法类似) + Python/Javscript开发效率 (写法类似)
-
应用:云计算 、微服务 、分布式系统
-
特点
-
便捷并发(Goroutine)
-
无视语言性跨平台编译(Windows可直接编译Linux可执行文件)
-
标准库强大 Web友好
-
静态类型语言
-
-
经典项目
- Docker:用于快速构建、测试和部署的应用程序
- Not Simple Queue (NSQ):高可用、分布式、实时的消息队列
- Prometheus:开源的监控、告警系统
- CockroachDB:分布式关系型数据库
使用Go语言的公司:Google、UBER、NETFLIX、FaceBook、哔哩哔哩、字节跳动......
2. 环境安装
2.1 Windows安装
Go语言环境配置
从官网下载并且完成安装

环境变量位于"计算机-属性/高级系统设置-环境变量"

在系统变量中path添加bin目录以及go.exe所在目录即可完成配置。
2.2 Linux安装
利用命令下载并解压
shell
cd /opt
wget https://studygolang.com/dl/golang/go1.21.3.linux-amd64.tar.gz
tar -xcf go1.21.3.linux-amd64.tar.gz
编辑环境变量(将bin目录添加到环境变量)
shell
vim /etc/profile
# 在文件后追加以下内容
export GOPROXY=httlps://goproxy.cn
export GOROOT=/opt/go
export PATH=$PATH:$GOROOT/bin
export GOPATH=/opt/go/pkg
export PATH=$PATH:$GOPATH/bin
# 退出并保存,刷新环境变量
source /etc/profile
利用``go version`检查安装是否成功

2.3 开发工具选择
3. 变量定义
Go语言的变量声明使用var关键字,这一点与Javascript比较类似。
3.1 先声明后赋值
go
package main
import "fmt"
func main() {
// 先声明(必须指定类型)
var name string
// 再赋值(必须使用 declared and not used: name)
name = "XXUU"
fmt.Println(name)
}



3.2 声明并赋值
go
package main
import "fmt"
func main() {
// 声明并赋值
var name string = "XXUU"
fmt.Println(name)
}

如果使用IDE,类型名称会变成昵称灰色,这意味着我们这里可以省略,即基本数据类型可以自动匹配:
go
package main
import "fmt"
func main() {
// 声明并赋值(允许自动匹配类型)
var name = "XXUU"
fmt.Println(name)
}

3.3 方法内变量赋值简写
全局变量不支持变量赋值简写
go
package main
import "fmt"
// 错误定义方法
// age1 := 13
局部变量支持变量赋值简写
go
package main
import "fmt"
func main() {
// 声明并赋值 短声名符号
name := "XXUU"
fmt.Println(name)
}
3.4 全局变量/常量
go
package main
import "fmt"
var age = 12
var (
s1 string = "1"
s2 string = "2"
)
const NAME = "myName"
const (
GENDER = "MAN!"
PHONE = "000-88888888"
)
3.5 全局变量/常量跨包访问
另一包中定义常量:

go
package pkg
const PkgConst1 string = "ABCDE"
const pkgConst2 string = "ABFFE"
在main中访问,只能访问到首字母为大写的变量(类似Java语言中public关键字与Python语言中前缀没有下划线):

go
package main
import (
"fmt"
"new_demo/pkg"
)
func main() {
// 这样就可以跨包访问变量了
fmt.Println(pkg.PkgConst1);
}

4. 输入输出
4.1 输出
4.1.1 普通输出
普通输出意味着不换行,该方法基本等同于Java的System.out.print()和Python的print()。
go
package main
import "fmt"
func main() {
fmt.Print("你好", "AA")
fmt.Print("你好", "BB")
}

4.1.2 跨行输出
跨行输出意味着伴随着换行输出,该方法基本等同于Java的System.out.println()和Python的print(..., "\n")。
go
package main
import "fmt"
func main() {
fmt.Println("你好", "AA")
fmt.Println("你好", "BB")
}

4.1.3 格式化输出
跨行输出意味着伴随着换行输出,该方法基本等同于Java的System.out.printf()、Python的print(f'{[定义变量]} {[定义变量]}')/print("%s %d" % ("a", 12))/print("{name} {age}".format(name="a", age=12)))。
go
package main
import "fmt"
func main() {
fmt.Printf("%v\n", "你好") // 可以作为任何值打印
fmt.Printf("%v %T\n", "大大", "小小") // 输出打印类型
fmt.Printf("%d\n", 3) // 适用于输出整数
fmt.Printf("%.2f\n", 3.1415926) // 适用于输出小数
fmt.Printf("%s\n", "大家好") // 适用于输出字符串
fmt.Printf("%#v\n", "") // 适用于输出空字符串
}

4.2 输入
与其他语言类似,Go语言同样提供控制台信息输入的功能。
go
package main
import "fmt"
func main() {
fmt.Println("请输入你的信息:")
var info string
fmt.Scan(&info)
fmt.Println(info)
}

go
package main
import "fmt"
func main() {
fmt.Println("请输入你的信息:")
var info string
n, err := fmt.Scan(&info)
fmt.Println(info)
}

go
package main
import "fmt"
func main() {
fmt.Println("请输入你的信息:")
var info int
n, err := fmt.Scan(&info)
fmt.Println(n, err, info)
}

5. 基本数据类型
Go语言有5种基本数据类型:
int:整数型float:浮点数型string:字符型bool:布尔型complex:复数型
5.1 整数型
Go语言支持的整数类型如下:
go
package main
import "fmt"
func main() {
var n1 uint8 = 2
var n2 uint16 = 2
var n3 uint32 = 2
var n4 uint64 = 2
var n5 uint = 2
var n6 int8 = 2
var n7 int16 = 2
var n8 int32 = 2
var n9 int64 = 2
var n10 int = 2
}
总结:
- 默认整数为int,不赋值int默认为0
- 带u为无符号,仅限正整数
- 无u为有符号,绝对值范围为带u数½(其中一位被表示正负 迫使一半范围转向负轴)
- 后面数字为二进制位数
- uint8 还有一个别名为byte(1byte=8bit)
- int类型大小取决于所使用的平台
5.2 浮点数型
Go语言支持的浮点数类型:float32 和 float64(其他语言的Double)
- float32的浮点数最大范围为3.4e38,可以使用常量定义:
math.MaxFloat32 - float64的浮点数最大范围为1.8e308,可以使用常量定义:
math.MaxFloat64
默认浮点数为float64
go
package main
import "fmt"
func main() {
var f1 = 3.1415926
}
5.3 字符型
5.3.1 字符
字符赋值为单引号
两个类型:byte(单字节字符)和 rune(多字节字符)
go
package main
import "fmt"
func main() {
var c1 = 'a'
var c2 = 97
fmt.Println(c1)
fmt.Println(c2)
fmt.Printf("%c %c\n", c1, c2)
var r1 rune = '中'
}
- Go中字符本质是一个整数,直接输出为Unicode UTF-8编码的码值
- 可以直接赋值数字,将其格式化为对应字符
- 故可将其视作数字进行运算
5.3.2 字符串
字符串赋值为双引号,字符串类型变量默认为"",即空字符串。
字符串类型
go
package main
import "fmt"
func main() {
var str string = "你好"
var str2 = "你好"
fmt.Println(str)
}
转义字符
go
package main
import "fmt"
func main() {
fmt.Printf("%c\n", r1)
fmt.Println("'单引号可以直接括住'")
fmt.Println("而斜线\\双引号则需要进行转义\"")
fmt.Println("制表符\t换行符\n回到行首\r也需要进行转义\"")
}
多行字符串
go
package main
import "fmt"
func main() {
var str = `今天
天气
真好
`
fmt.Println(str)
}
5.4 布尔型
布尔类型只有 true(真) 和 false(假) 两个值
- 布尔类型变量默认为false
- Go语言中布尔类型不允许由数字转换,同时也不允许其直接参与运算
go
package main
import "fmt"
func main() {
var b bool = true
fmt.Println(b)
}
5.5 复数型
复数型用complex关键字定义,其常用于数学计算。
go
package main
import (
"fmt"
)
func main() {
// 获取实部虚部
c := complex(3, 4) // 创建一个复数 3 + 4i
fmt.Println(real(c)) // 输出: 3.0
fmt.Println(imag(c)) // 输出: 4.0
}

6. 容器
6.1 数组
数组标准定义
go
package main
import "fmt"
func main() {
// 定义数组
var list [3]string = [3]string{"A", "B", "C"}
fmt.Println(list)
fmt.Println(list[0]) // 索引取值
}
同样,数组可以省略前面的类型声明
go
// 定义数组
var list [3]string = [3]string{"A", "B", "C"}
var list = [3]string{"A", "B", "C"}
Go语言中不支持负向索引(如Python中arr[-1]实际上等同于arr[len(arr) - 1])
Go语言中数组不支持更改其长度,所以推出了"可变数组"(Java中utils.list以及Python中的list),即可变数组。
go
package main
import "fmt"
func main() {
// 定义数组
var arr [3]string
arr = [3]string{"A", "B", "C"}
fmt.Println(arr)
// 定义可变数组(这种定义方式看起来像是未指定长度的数组)
var list []string
list = append(list, "A")
list = append(list, "B")
fmt.Println(list)
}

数组非nil初始化方法
go
package main
import "fmt"
func main() {
// 1
var list []string = []string{}
// 2
var list = []string{}
// 3
list := []string{}
// 4
var list []string
list = make([]string, 0) // 0表示指定长度
}
数组切片及排序
go
package main
import "fmt"
func main() {
var list = []int{0, 1, 2}
var slice = list[:]
fmt.Println(slice)
var slice2 = list[1:]
fmt.Println(slice2)
}

数组排序
go
package main
import (
"fmt"
"sort"
)
func main() {
var list = []int{1, 0, 2}
fmt.Println("原数组:", list)
sort.Ints(list)
fmt.Println("升序数组:", list)
sort.Sort(sort.Reverse(sort.IntSlice(list)))
fmt.Println("降序数组:", list)
}

利用切片功能删除元素
go
var list = []int{1, 0, 2}
fmt.Println("原数组:", list)
sort.Ints(list)
fmt.Println("升序数组:", list)
sort.Sort(sort.Reverse(sort.IntSlice(list)))
fmt.Println("降序数组:", list)
list = append(list[:1], list[2:]...)
fmt.Println("降序后数组删除元素:", list)

6.2 映射
映射标准定义
go
package main
import "fmt"
func main() {
var userMap map[int]string = map[int]string{
0: "用户A",
1: "用户B",
}
fmt.Println(userMap)
}
同样,映射可以省略前面的类型声明
go
package main
import "fmt"
func main() {
var userMap = map[int]string{
0: "用户A",
1: "用户B",
}
fmt.Println(userMap)
}

根据key拿取映射中的value:
go
import "fmt"
func main() {
var userMap map[string]string = map[string]string{
"A0": "用户A",
"A1": "用户B",
}
fmt.Println(userMap["A0"])
}

通过双值接收,判断是定义空串还是不存在的默认空串。
go
package main
import "fmt"
func main() {
var userMap map[string]string = map[string]string{
"A0": "用户A",
"A1": "用户B",
"A2": "",
}
fmt.Println(userMap["A0"])
fmt.Printf("%#v\n", userMap["A2"])
fmt.Printf("%#v\n", userMap["A3"])
value2, ok2 := userMap["A2"]
value3, ok3 := userMap["A3"]
fmt.Println(value2, ok2)
fmt.Println(value3, ok3)
}
可以使用delete方法删除元素
go
delete(userMap, "A2")
映射类型必须初始化,两种初始化方法
go
var myMap = map[string]string{}
var myMap = make(map[string]string)
映射默认线程不安全,这个后面会提到。
7. 语句控制
7.1 逻辑控制(IF & SWTICH)
7.1.1 IF-ELSEIF-ELSE
go
package main
import "fmt"
func main() {
var age = 21
if age <= 0 {
fmt.Println("未出生")
} else if age > 0 && age <= 18 {
fmt.Println("未成年")
} else if age > 19 && age <= 35 {
fmt.Println("青年")
} else {
fmt.Println("中年及以上")
}
}

7.1.2 SWTICH-CASE
Go语言的 SWTICH-CASE 基本与其他语言无异,唯一的区别在于无需break,如需穿透执行可使用fallthrough关键字。
数值判断
go
package main
import "fmt"
func main() {
var age = 21
switch {
case age <= 0:
fmt.Println("未出生")
case age <= 18:
fmt.Println("未成年")
case age <= 35:
fmt.Println("青年")
default:
fmt.Println("中年及以上")
}
}

类型判断
go
package main
import "fmt"
func main() {
var x interface{}
switch i := x.(type) {
case nil:
fmt.Printf("类型 :%T", i)
case int:
fmt.Printf("int 型")
case float64:
fmt.Printf("float64 型")
case func(int) float64:
fmt.Printf("func(int) 型")
case bool, string:
fmt.Printf("bool / string 型")
default:
fmt.Printf("未知型")
}
}

判断穿透
go
package main
import "fmt"
func main() {
var num = 2
switch num {
case 0:
fmt.Println("1")
fallthrough
case 1:
fmt.Println("2")
fallthrough
case 2:
fmt.Println("3")
fallthrough
case 3:
fmt.Println("4")
fallthrough
case 4:
fmt.Println("5")
fallthrough
default:
fmt.Println("6")
}
}

7.2 循环控制(FOR)
7.2.1 计数循环
以求和为例:
go
package main
import "fmt"
func main() {
var sum = 0
for i := 0; i <= 100; i++ {
sum += i
}
fmt.Println(sum)
}

7.2.2 类while循环
Go语言中没有while循环,但是可以利用for循环执行相同的功能:
go
package main
import "fmt"
func main() {
var a = 1
for a < 10 {
a++
fmt.Println(a)
}
}

无限循环
go
package main
import "fmt"
func main() {
for true {
fmt.Printf("这是无限循环。\n");
}
}

7.2.3 range 循环
Go语言的循环控制允许使用range进行,这一点与Python比较类似,形式上类似range,但用法更接近 迭代器(iterator),Java也有类似的 功能实现。
go
package main
import "fmt"
func main() {
var list = []string{"你好", "我是ABC"}
for index, item := range list {
fmt.Println(index, item)
}
}

7.2.4 continue和break
功能与其他语言类似:
continue见了换路绕开(后面不执行 进行下一次循环)break见了再也不见(退出最近的内层循环)goto跳转到被标记的语句(一般不用)
8. 函数
8.1 函数介绍
Go语言函数关键字为func,并支持函数嵌套,基本的函数结构如下:
go
type Student struct {
Name string
Age int
}
func ([方法所属结构体])funcName([输入参数及类型]) ([返回参数类型])([嵌套返回参数类型]) {
// 执行结构
}
当函数前几项类型一致时,可以仅保留最后一项的参数类型:
go
// 初始形式
func funcName1(param1 string, param2 string, param3 string, param4 int) string {
return "ABC"
}
// 简化为如下形式
func funcName2(param1, param2, param3 string, param4 int) string {
return "ABC"
}
定义一个用于求和的方法:
go
package main
import "fmt"
func main() {
/* 定义局部变量 */
var a int = 100
var b int = 200
var result int
/* 调用函数并返回求和值 */
result = sum(a, b)
fmt.Printf( "二者求和为 : %d\n", result )
}
func sum(num1, num2 int) int {
return num1 + num2
}

函数也可以返回多个值,并直接进行解包,这一点与Python语言类似。
go
package main
import "fmt"
func main() {
vA, vB := twoReturn()
fmt.Println( vA, vB )
}
func twoReturn() (string, string) {
return "ABC", "BCD"
}


闭包
设计一个函数,先传一个参数表示延时延缓,后面再次将求和函数整体传参。
go
/* 函数返回数组/切片内数字求和 */
func awaitAdd(awaitTime int) func(...int) int {
time.Sleep(time.Duration(awaitTime) * time.Second)
return func(numList ...int) int {
var sum int
for _, i2 := range numList {
sum += i2;
}
return sum
}
}
调用检查其延缓效果
go
package main
import "fmt"
func main() {
t1 := time.Now()
sum := awaitAdd(2)(1,2,3)
subTime := time.Since(t1)
fmt.Println(sum, subTime)
}

8.2 值传递 & 引用传递
函数值传递
go
package main
import "fmt"
func main() {
str := "ABC"
fmt.Printf("原有变量:%p\n", &str)
str2 := funcDefine(str)
fmt.Printf("返回变量:%p\n", &str2)
}
func funcDefine(str string) string {
fmt.Printf("方法内变量:%p\n", &str)
return str
}

函数引用传递
go
package main
import "fmt"
func main() {
str := "ABC"
fmt.Printf("原有变量:%p\n", &str)
str2 := funcDefine(&str)
fmt.Printf("返回变量:%p\n", &str2)
}
func funcDefine(str *string) string {
fmt.Printf("方法内变量:%p\n", str)
return *str
}

8.3 init函数 & defer函数
8.3.1 init函数
init()函数可用于初始化,其执行先于main()函数,自动被调用,init()函数不能作为参数传入,且无法传参返回(无意义)。
Go文件可以有多个init函数,并且依照先后顺序执行,形式上可视作完整init()分散在各地的代码碎片,这种灵活的组合方式提升了代码可读性。

go
package main
import (
"fmt"
)
// main方法后的init
func init() {
fmt.Println("INIT_1")
}
// main方法后的init
func init() {
fmt.Println("INIT_2")
}
func main() {
fmt.Println("main")
}
// main方法后的init
func init() {
fmt.Println("INIT_3")
}
为更好展现运行顺序,我们为全局变量赋值
go
package main
import (
"fmt"
)
var number int
func init() {
number = 100
fmt.Println("INIT_1")
}
func init() {
fmt.Println("INIT_2")
fmt.Println(number)
}
func main() {
number = 50
fmt.Println("main")
fmt.Println(number)
}
func init() {
number = 80
fmt.Println("INIT_3")
fmt.Println(number)
}

8.3.2 defer函数
defer()函数在return之前被调用,多个defer按照先进后出(栈)的方式执行,举例return近的的defer优先被执行。
但defer使用方法内的变量仍需定义在defer之前,defer可用于进行资源清理。
go
package main
import "fmt"
func main() {
// 定义匿名函数及函数体并调用(最后一个括号表示调用)
defer func() {
fmt.Println("DEFER_4")
}()
// 调用显式函数(最后一个括号表示调用)
defer fmt.Println("DEFER_3")
defer deferUse2()
defer deferUse1()
return
}
func deferUse1() {
fmt.Println("DEFER_1")
}
func deferUse2() {
fmt.Println("DEFER_2")
}


9. 结构体
9.1 结构体介绍
go
type 结构体名称 struct {
名称 类型 //成员表属性
}
定义结构体并创建实例
go
package main
import "fmt"
// Student 定义结构体
type Student struct {
Name string
Age int
}
func main() {
stu1 := Student{Name: "学生A", Age: 12}
fmt.Println(stu1)
}

定义实例方法并调用
go
package main
import "fmt"
type Student struct {
Name string
Age int
}
func (s Student) study() {
fmt.Println(s.Name, " 正在学习")
}
func main() {
stu1 := Student{Name: "学生A", Age: 12}
stu1.study()
stu2 := Student{Name: "学生R", Age: 16}
stu2.study()
}

为结构体增加附属方法:【对比区别】
go
func (s Student)getName() (string) {
return s.Name;
}
func (s *Student)getRealName() (string){
return s.Name;
}
9.2 继承 & 结构体指针
Go语言的继承形式上更接近其他面向对象语的组合,因为形式上更接近组合,故将班级作为学生的属性定义。
go
package main
import "fmt"
type Class struct {
Name string
}
type Student struct {
Class
Name string
Age int
}
func (s Student) info() {
fmt.Printf("%s 所属班级:%s\n", s.Name, s.Class)
}
func main() {
class1 := Class{Name: "三年一班"}
stu1 := Student{Name: "学生A", Age: 12, Class: class1}
stu1.info()
stu2 := Student{Name: "学生R", Age: 16, Class: class1}
stu2.info()
}

以上示例中,Class作为实例输出了(花括号),这不符合我们想要输出的结果。
Go语言也支持直接选择"属性内属性",示例如下。
go
package main
import "fmt"
type Class struct {
Name string
ClassName string
}
type Student struct {
Class
Name string
Age int
}
func (s Student) info() {
fmt.Printf("%s 所属班级:%s\n", s.Name, s.Class.Name)
fmt.Printf("%s 所属班级:%s\n", s.Name, s.ClassName)
}
func main() {
class1 := Class{Name: "三年一班", ClassName: "三年一班"}
stu1 := Student{Name: "学生A", Age: 12, Class: class1}
stu1.info()
stu2 := Student{Name: "学生R", Age: 16, Class: class1}
stu2.info()
}

而通过方法更改对象属性则需要传递其引用,否则将等效于在方法内临时拷贝了一份副本,原始结构体实例属性无法更改成功。
go
package main
import "fmt"
type Class struct {
Name string
ClassName string
}
type Student struct {
Class
Name string
Age int
}
func (s Student) setName() {
s.Name += " 学霸"
fmt.Println(s.Name)
}
func (s Student) info() {
fmt.Printf("%s 所属班级:%s\n", s.Name, s.Class.Name)
fmt.Printf("%s 所属班级:%s\n", s.Name, s.ClassName)
}
func main() {
class1 := Class{Name: "三年一班", ClassName: "三年一班"}
stu1 := Student{Name: "学生A", Age: 12, Class: class1}
stu1.info()
stu2 := Student{Name: "学生R", Age: 16, Class: class1}
stu2.setName()
stu2.info()
}

只需要将引用传入,便可解决此问题。
go
func (s *Student) setName() {
s.Name += " 学霸"
fmt.Println(s.Name)
}

9.3 结构体tag
在Go语言中,json:"..."是结构体字段上的**结构体标签(Tag),用于控制encoding/json包在 序列化(Marshal)和反序列化(Unmarshal)**过程中如何映射字段与JSON键。
结构体tag常见用途:
- 别名
- 隐藏
go
package main
import (
"encoding/json"
"fmt"
)
type Student struct {
Name string
Age int
SecretInfo string
}
func main() {
stu := Student{Name: "学生R", Age: 16, SecretInfo: "秘密基地在冥王星北极点冰冠下,有我数万年前星际旅行时留下的秘密宝藏。"}
byteData, _ := json.Marshal(stu)
fmt.Println(string(byteData))
}

go
type Student struct {
Name string `json:"name"`
Age int `json:"age"`
SecretInfo string `json:"-"`
}

防止属性为默认值时输出的选项。
go
package main
import (
"encoding/json"
"fmt"
)
type Student struct {
Name string `json:"name,omitempty"`
Age int `json:"age,omitempty"`
SecretInfo string `json:"-"`
}
func main() {
stu := Student{Name: "学生R", Age: 16, SecretInfo: "秘密基地在冥王星北极点冰冠下,有我数万年前星际旅行时留下的秘密宝藏。"}
byteData, _ := json.Marshal(stu)
fmt.Println(string(byteData))
}

10. 自定义数据类型 & 接口
10.1 自定义数据类型
Go语言中,可以利用type关键字定义新的类型,可定义类型包括基本数据类型、结构体、函数等。利用自定义数据类型可以更好地封装数据,使代码易维护。
go
// 网络动态码返回函数
func webServer(name string) int {
if name == "1" {
return 1001
}
if name == "2" {
return 1002
}
return 0
}
这样就会出现经典的"魔鬼数字"问题,且代码非常难以维护,一种好的解决方法如下。
go
package main
// 自定义类型
type Code int
// 自定义类型则可以自定义方法
func (c Code) getMsg() string {
switch c {
case SuccessCode:
return "成功"
case ServiceErrCode:
return "服务错误"
case NetworkErrCode:
return "网络错误"
default:
return "其他"
}
}
// 自定义类型则可以自定义方法
func (c Code) getCodeWithMsg() (code Code, msg string) {
return c, c.getMsg()
}
const (
SuccessCode Code = 0
ServiceErrCode Code = 1001
NetworkErrCode Code = 1002
)
// 网络动态码返回函数
func webServer(name string) (code Code, msg string) {
if name == "1" {
return ServiceErrCode.getCodeWithMsg()
}
if name == "2" {
return NetworkErrCode.getCodeWithMsg()
}
return SuccessCode.getCodeWithMsg()
}
// main方法
func main() {
webServer("1")
}
10.2 类型别名
类似自定义类型,但本质上是原始类型的"小名",不用转换类型。
go
package main
import "fmt"
type MyCode int
type MyAlias = int
const MyCodeInstance MyCode = 1
const MyAliasCodeInstance MyAlias = 1
func main() {
fmt.Printf("%v, %T\n", MyCodeInstance, MyCodeInstance)
fmt.Printf("%v, %T\n", MyAliasCodeInstance, MyAliasCodeInstance)
}

10.3 接口
接口是一组见包含方法名、参数、返回值的未具体实现的方法的集合。
go
package main
import "fmt"
// 接口是 值+原始类型
type SingInterface interface {
Sing()
GetName() string
}
type Chicken struct {
Name string
}
func (c Chicken) Sing() {
fmt.Println(c.Name, "在唱歌")
}
func (c Chicken) GetName() string {
return c.Name
}
type Cat struct {
Name string
}
func (c Cat) Sing() {
fmt.Println(c.Name, "在唱歌")
}
func (c Cat) GetName() string {
return c.Name
}
// sing
func sing(c SingInterface) {
// 类型断言
//ch, ok := c.(Chicken)
c.Sing()
fmt.Println(c.GetName())
// 一般做法
switch server := c.(type) {
case Chicken:
fmt.Println(server)
case Cat:
fmt.Println(server)
default:
fmt.Println("其他")
}
}

空接口等同于任意类型:
go
// 空接口:可以接受任何类型
type EmptyInterface interface{} // type any interface{}
// 简介写法:匿名空接口
func Print(val interface{}) {
fmt.Println(val)
}
func PrintSame(val any) {
fmt.Println(val)
}