Go基础3:函数、结构体、方法、接口
1. 函数
Go语言的函数属于“一等公民”(first-class),也就是说:
- 函数本身可以作为值进行传递。
- 支持匿名函数和闭包(closure)。
- 函数可以满足接口。
1.1 函数返回值
同一种类型返回值
func typedTwoValues() (int, int) {
return 1, 2
}
a, b := typedTwoValues()
fmt.Println(a, b)
带变量名的返回值
func named Ret Values() (a, b int) {
a = 1
b = 2
return
}
函数使用命名返回值时,可以在return中不填写返回值列表,如果填写也是可行的
函数中的参数传递
Go语言中传入和返回参数在调用和返回时都使用值传递,这里需要注意的是指针、切片和map等引用型对象指向的内容在参数传递中不会发生复制,而是将指针进行复制,类似于创建一次引用。
函数变量
在Go语言中,函数也是一种类型,可以和其他类型一样被保存在变量中
func fire() {
fmt.Println("fire")
}
func main() {
var f func() //将变量f声明为func()类型,此时f就被俗称为“回调函数”。此时f的值为nil。
f = fire
f()
}
1.2 匿名函数——没有函数名字的函数
Go语言支持匿名函数,即在需要使用函数时,再定义函数,匿名函数没有函数名,只有函数体,函数可以被作为一种类型被赋值给函数类型的变量,匿名函数也往往以变量方式被传递。
在定义时调用匿名函数
匿名函数可以在声明后调用,例如:
func(data int) {
fmt.Println("hello", data)
}(100)
将匿名函数赋值给变量
匿名函数体可以被赋值,例如:
// 将匿名函数体保存到f()中
f := func(data int) {
fmt.Println("hello", data)
}
// 使用f()调用
f(100)
匿名函数用作回调函数
使用时再定义匿名函数,不使用先在被调用函数里面进行声明,这就是回调精髓
// 遍历切片的每个元素,通过给定函数进行元素访问
func visit(list []int, f func(int)) {
for _, v := range list {
f(v)
}
}
func main() {
// 使用匿名函数打印切片内容
visit([]int{1, 2, 3, 4}, func(v int) {
fmt.Println(v)
})
}
可变参数——参数数量不固定的函数形式
1.所有参数都是可变参数:fmt.Println
func Println(a ...interface{}) (n int, err error) {
return Fprintln(os.Stdout, a...)
}
fmt.Println在使用时,传入的值类型不受限制,例如:
fmt.Println(5, "hello", &struct{ a int }{1}, true)
当可变参数为
interface{}
类型时,可以传入任何类型的值
2.部分参数是可变参数:fmt.Printf
fmt.Printf的第一个参数为参数列表,后面的参数是可变参数:
func Printf(format string, a ...interface{}) (n int, err error) {
return Fprintf(os.Stdout, format, a...)
}
------------------------------------------------------
fmt.Printf("pure string\n")
fmt.Printf("value: %v %f\n", true, math.Pi)
1.3 闭包
闭包可以理解成定义在函数内部的一个函数。本质上,闭包是函数内部和函数外部连接起来的桥梁。
简单来说,闭包=函数+引用环境
func main() {
var f = add()
fmt.Printf("f(10): %v\n", f(10))
fmt.Printf("f(20): %v\n", f(20))
// f(10): 10
// f(20): 30
}
func add() func(int) int {
var x int
return func(y int) int {
x += y
return x
}
}
1.4 defer语句
defer语句将其后面跟随的语句进行延迟处理,被defer的语句按先进后出的方式执行(最先defer的语句最后执行,后被defer的语句先执行)。
特性:
- 关键字defer用于注册延迟调用
- 直到调用return之前才执行(故可用来作资源清理)
- 多个defer语句,FILO方式执行
- defer中的变量,在defer声明时就定义了
用途:
- 关闭文件句柄
- 锁资源释放
- 数据库连接释放
处理运行时发生的错误
Go语言的错误处理思想及设计包含以下特征:
● 一个可能造成错误的函数,需要返回值中返回一个错误接口(error)。如果调用是成功的,错误接口将返回nil,否则返回错误。
● 在函数调用后需要检查错误,如果发生错误,进行必要的错误处理。
错误接口的定义格式
error是Go系统声明的接口类型,代码如下:
type error interface {
Error() string // 返回错误的具体描述.
}
所有符合Error() string格式的接口都能实现错误接口。
定义一个错误
在Go语言中,使用errors包进行错误的定义,格式如下:
var err = errors.New("this is an error")
错误字符串由于相对固定,一般在包作用域声明,应尽量减少在使用时直接使用errors.New返回。
宕机(panic)——程序终止运行
1 手动触发宕机
Go语言可以在程序中手动触发宕机,让程序崩溃,这样开发者可以及时地发现错误,同时减少可能的损失。
Go语言程序在宕机时,会将堆栈和goroutine信息输出到控制台,所以宕机也可以方便地知晓发生错误的位置。
package main
func main() {
panic("crash")
}
panic()的参数可以是任意类型,
当panic()触发的宕机发生时,panic()后面的代码将不会被运行,但是在panic()函数前面已经运行过的defer语句依然会在宕机发生时发生作用,
1.5 宕机恢复(recover)——防止程序崩溃
无论是代码运行错误由Runtime层抛出的panic崩溃,还是主动触发的panic崩溃,都可以配合defer和recover实现错误捕捉和恢复,让代码在发生崩溃后允许继续运行。
Go没有异常系统,其使用panic触发宕机类似于其他语言的抛出异常,那么recover的宕机恢复机制就对应try/catch机制。
panic和recover的关系:
● 有panic没recover,程序宕机。
● 有panic也有recover捕获,程序不会宕机。执行完对应的defer后,从宕机点退出当前函数后继续执行。
提示:虽然panic/recover能模拟其他语言的异常机制,但并不建议代表编写普通函数也经常性使用这种特性。
2. 结构体
结构体成员是由一系列的成员变量构成,这些成员变量也被称为“字段”。字段有以下特性:
● 字段拥有自己的类型和值。
● 字段名必须唯一。
● 字段的类型也可以是结构体,甚至是字段所在结构体的类型。
Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。
Go语言的结构体与“类”都是复合结构体,但Go语言中结构体的内嵌配合接口比面向对象具有更高的扩展性和灵活性。
Go语言不仅认为结构体能拥有方法,且每种自定义类型也可以拥有自己的方法。
2.1 定义与给结构体赋值
基本形式:
type Point struct {
X int
Y int
}
var p Point
p.X = 10
p.Y = 20
结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存
创建指针类型的结构体:
type Player struct {
name string
age int
}
p = new(Player)
p.name = "james"
p.age = 40
取结构体的地址实例化:
//使用结构体定义一个命令行指令(Command),指令中包含名称、变量关联和注释等
type Command struct {
name string
Var *int
comment string
}
var version int = 1
cmd := &Command{}
cmd.name = "version"
cmd.Var = &version
cmd.comment = "show version"
使用键值对填充结构体:
type People struct {
name string
child *People
}
relation := &People{
name: "爷爷"
child: &People{
name: "爸爸"
child: &People{
name: "我"
},
}
}
3. 方法
Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收器(Receiver)。
如果将特定类型理解为结构体或“类”时,接收器的概念就类似于其他语言中的this
或者self
。
结构体方法
创建一个背包Bag
结构体为其定义把物品放入背包的方法insert
:
type Bag struct {
items[] int
}
func (b *Bag) insert(itemid int) {
b.items = append(b.items, itemid)
}
func main() {
b := new(Bag)
b.insert(1001)
}
(b*Bag)
表示接收器,即Insert
作用的对象实例。每个方法只能有一个接收器。
接收器
接收器是方法作用的目标
接收器根据接收器的类型可分:
- 指针接收器
- 非指针接收器
- 两种接收器在使用时会产生不同的效果。根据效果的不同,两种接收器会被用于不同性能和功能要求的代码中。
指针接收器
由于指针的特性,调用方法时,修改接收器指针的任意成员变量,在方法结束后,修改都是有效的。
// 定义属性结构
type Property struct {
value int
}
// 设置属性值方法
func (p *Property) setVal(val int) {
p.value = val
}
// 获取属性值方法
func (p *Property) getVal() int {
return p.value
}
func main() {
p := new(Property)
p.value = 123
fmt.Println(p.getVal())
p.setVal(666)
fmt.Println(p.getVal())
}
非指针类型接收器
当方法作用于非指针接收器时,Go语言会在代码运行时将接收器的值复制一份。在非指针接收器的方法中可以获取接收器的成员值,但修改后无效。
type Point struct {
x, y int
}
func (p Point) add(other Point) Point {
return Point{p.x + other.x, p.y + other.y}
}
func main() {
// 初始化点
p1 := Point{1, 1}
p2 := Point{2, 2}
res := p1.add(p2)
fmt.Println(res)
p3 := Point{3, 3}
p4 := p1.add(p2).add(p3)
fmt.Println(p4)
}
指针接收器和非指针接收器的使用:
指针和非指针接收器的使用在计算机中,小对象由于值复制时的速度较快,所以适合使用非指针接收器。大对象因为复制性能较低,适合使用指针接收器,在接收器和参数间传递时不进行复制,只是传递指针。
4. 接口
接口是双方约定的一种合作协议。接口实现者不需要关心接口会被怎样使用,调用者也不需要关心接口的实现细节。接口是一种类型,也是一种抽象结构,不会暴露所含数据的格式、类型及结构。
声明接口
type 接口类型名 interface {
方法1(参数列表) 返回值
...
}
Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer,有关闭功能的接口叫Closer等
方法名:当方法名首字母是大写时,且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
io包中提供的Writer接口:
type Writer interface {
Write(p []type) (n int, err error)
}
实现接口
实现接口的条件:
- 接口的方法与实现接口的类型方法格式一致
- 接口中所有方法均被实现
例:为了抽象数据写入的过程,定义Data Writer接口来描述数据写入需要实现的方法。
// 定义一个数据写入器接口
type DataWriter interface {
WriteData(data interface{}) error
}
// 定义文件结构,用于实现DataWriter
type file struct {
}
// 实现DataWriter接口的方法
func (d *file) WriteData(data interface{}) error {
// 模拟写入数据
fmt.Println("Write Data:", data)
return nil
}
func main() {
// 实例化file
f := new(file)
// 声明一个DataWriter接口
var writer DataWriter
// 将接口赋值,也就是*file
writer = f
writer.WriteData("one line data")
}
Go语言的接口实现是隐式的,无须让实现接口的类型写出实现了哪些接口。这个设计被称为非侵入式设计。
Go基础3:函数、结构体、方法、接口的更多相关文章
- 换个语言学一下 Golang (9)——结构体和接口
基本上到这里的时候,就是上了一个台阶了.Go的精华特点即将展开. 结构体定义 上面我们说过Go的指针和C的不同,结构体也是一样的.Go是一门删繁就简的语言,一切令人困惑的特性都必须去掉. 简单来讲,G ...
- Go 结构体方法
#### Go 结构体方法本来今天有些事情忙的不准备更新内容了,后来提前完成了, 所以还是要更新了; 毕竟坚持本就是一件不容易的事情!加油,相信不管是大家还是我,都有一些事情想要做,那就坚持吧,剩下的 ...
- go结构体方法
Golang中的方法是作用在特定类型的变量上,因此自定义类型,都可以有方法,而不仅仅是struct. 定义格式 func (var *Struct_Name) FuncName( var0, var1 ...
- Golang 笔记 2 函数、结构体、接口、指针
一.函数 Go中函数是一等(first-class)类型.我们可以把函数当作值来传递和使用.Go中的函数可以返回多个结果. 函数类型字面量由关键字func.由圆括号包裹声明列表.空格以及可以由圆括号 ...
- Go语言基础六:结构体和方法
结构体 结构体是一个由用户定义的复合类型,它由一系列属性组成,每个属性都有自己的类型和值.Go语言中数组可以存储同一类型的数据,但在结构体中用户可以为不同项定义不同(或相同)的数据类型.结构体是值类型 ...
- ARM-Linux S5PV210 UART驱动(3)----串口核心层、关键结构体、接口关系
尽管一个特定的UART设备驱动完全可以按照tty驱动的设计方法来设计,即定义tty_driver并实现tty_operations其中的成员函数,但是Linux已经在文件serial_core.c中实 ...
- Go语言学习笔记(四)结构体struct & 接口Interface & 反射
加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959 结构体struct struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套: go中的struc ...
- GO语言系列(五)- 结构体和接口
结构体(Struct) Go中struct的特点 1. 用来自定义复杂数据结构 2. struct里面可以包含多个字段(属性) 3. struct类型可以定义方法,注意和函数的区分 4. struct ...
- golang结构体、接口、反射
struct结构体 struct用来自定义复杂数据结构,可以包含多个字段属性,可以嵌套; go中的struct类型理解为类,可以定义方法,和函数定义有些许区别; struct类型是值类型. struc ...
- Go语言学习笔记(四)结构体struct & 接口Interface & 反射reflect
加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959 结构体struct struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套: go中的struc ...
随机推荐
- C++ - main()和_tmain(int argc, _TCHAR* argv[]) 的详细区别
一些程序中会发现主函数不是int main(),而是int _tmain(),而且头文件也不是<iostream>而是<stdafx.h>. 首先,这个_tmain()是为了支 ...
- JavaScript实现简单轮播图动画
运行效果: 源代码: <!DOCTYPE html> <html lang="zh"> <head> <meta charset=&quo ...
- Javascript中数组的判断方法
摘要: 1.数组检测的方法: 1) typeof . 2) instanceof . 3) constructor . 4) Object.prototype.toString. 5) Array.i ...
- (动态模型类,我的独创)Django的原生ORM框架如何支持MongoDB,同时应对客户使用时随时变动字段
1.背景知识 需要开发一个系统,处理大量EXCEL表格信息,各种类别.表格标题多变,因此使用不需要预先设计数据表结构的MongoDB,即NoSQL.一是字段不固定,二是同名字段可以存储不同的字段类型. ...
- Spring的事务控制-基于xml方式
介绍:该程序模拟了转账操作,即Jone减少500元,tom增加500元 1.导入坐标 <dependency> <groupId>junit</groupId> & ...
- 怎么快速找出帝国CMS数据库配置文件路径及迁移网站后修改技巧!
首先,我们要了解一下帝国CMS整个目录结构,只有了解清楚结构,我们才有可能快速找到自己想要的文件,比如:帝国CMS数据库配置文件路径! 帝国CMS目录结构介绍 / 系统根目录├d/ 附件和数据存放目录 ...
- springboot读取配置文件赋值给静态变量
1.实现InitializingBean接口,重写afterPropertiesSet方法,将@Value赋值给成员变量的属性赋值给静态变量,示例如下: /** * @Classname FileUt ...
- C++篇:第四章_函数_知识点大全
C++篇为本人学C++时所做笔记(特别是疑难杂点),全是硬货,虽然看着枯燥但会让你收益颇丰,可用作学习C++的一大利器 五.函数 (一)函数使用规则 函数的定义不能嵌套但调用可以嵌套 在函数调用时,如 ...
- partOne当堂测试代码
/* ScoreInformation类*/public class ScoreInformation { private String stunumber;//学号 private String n ...
- 如何使用 pyqt 实现 Groove 音乐播放器
前言 Win10 自带了一款很好用的音乐播放器 -- Groove 音乐,但是只能支持本地音乐的播放.2020年3月底到4月底,自己一直在学习 PyQt5,然后从 2020年5月开始,着手使用 PyQ ...