Go语言学习函数+结构体+方法+接口

目录
  • 1. 函数
    • 1.1 函数返回值
      • 同一种类型返回值
      • 带变量名的返回值
      • 函数中的参数传递
      • 函数变量
    • 1.2 匿名函数——没有函数名字的函数
      • 在定义时调用匿名函数
      • 将匿名函数赋值给变量
      • 匿名函数用作回调函数
      • 可变参数——参数数量不固定的函数形式
    • 1.3 闭包
    • 1.4 defer语句
      • 处理运行时发生的错误
    • 1.5 宕机恢复(recover)——防止程序崩溃
  • 2. 结构体
    • 2.1 定义与给结构体赋值
  • 3. 方法
    • 3.1 结构体方法
    • 3.2 接收器
      • 指针接收器
      • 非指针类型接收器
  • 4. 接口
    • 4.1 声明接口
    • 4.2 实现接口

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)
        })
   }

可变参数——参数数量不固定的函数形式

所有参数都是可变参数: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{} 类型时,可以传入任何类型的值

部分参数 是可变参数: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)——程序终止运行

手动触发宕机

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 。

3.1 结构体方法

创建一个背包 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 作用的对象实例。每个方法只能有一个接收器。

3.2 接收器

接收器是方法作用的目标

接收器根据接收器的类型可分:

  • 指针接收器
  • 非指针接收器
  • 两种接收器在使用时会产生不同的效果。根据效果的不同,两种接收器会被用于不同性能和功能要求的代码中。

指针接收器

由于指针的特性,调用方法时,修改接收器指针的任意成员变量,在方法结束后,修改都是有效的。

// 定义属性结构
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. 接口

接口是双方约定的一种合作协议。接口实现者不需要关心接口会被怎样使用,调用者也不需要关心接口的实现细节。 接口是一种类型,也是一种抽象结构,不会暴露所含数据的格式、类型及结构。

4.1 声明接口

type 接口类型名 interface {
	方法1(参数列表) 返回值
	...
}

Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer,有关闭功能的接口叫Closer等

方法名:当方法名首字母是大写时,且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。

io包中提供的Writer接口:

type Writer interface {
	Write(p []type) (n int, err error)
}

4.2 实现接口

实现接口的条件:

  • 接口的方法与实现接口的类型方法格式一致
  • 接口中所有方法均被实现

例:为了抽象数据写入的过程,定义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语言学习函数+结构体+方法+接口的文章就介绍到这了,更多相关Go 函数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Go语言使用swagger生成接口文档的方法

    swagger介绍 Swagger本质上是一种用于描述使用JSON表示的RESTful API的接口描述语言.Swagger与一组开源软件工具一起使用,以设计.构建.记录和使用RESTful Web服务.Swagger包括自动文档,代码生成和测试用例生成. 在前后端分离的项目开发过程中,如果后端同学能够提供一份清晰明了的接口文档,那么就能极大地提高大家的沟通效率和开发效率.可是编写接口文档历来都是令人头痛的,而且后续接口文档的维护也十分耗费精力. 最好是有一种方案能够既满足我们输出文档的需要又能

  • python 调用Google翻译接口的方法

    一.网页分析 打开谷歌翻译链接:https://translate.google.com/ 按F12,点击network.在左侧输入"who are you" 可以看到,请求的链接为: https://translate.google.com/_/TranslateWebserverUi/data/batchexecute?rpcids=MkEWBc&f.sid=-2609060161424095358&bl=boq_translate-webserver_202012

  • Go语言七篇入门教程三函数方法及接口

    目录 1. 函数 2. 方法 3. 接口 如何学习Go 参考书籍: <go语言程序设计> 1. 函数 每个函数声明都包含一个名字,一个形参列表,一个可选的返回列表以及函数体: func name(parameter-list)(result-list){ body } 形参列表:指定另一组变量的参数名和参数类型,这些局部变量都由调用者提供的提供的实参传递而来的. 返回列表:指定了函数返回值的类型.当函数返回一个未命名的返回值或者没有返回值的时候,返回列表的圆括号可以忽略. func FanOn

  • Django-Scrapy生成后端json接口的方法示例

    网上的关于django-scrapy的介绍比较少,该博客只在本人查资料的过程中学习的,如果不对之处,希望指出改正: 以后的博客可能不会再出关于django相关的点: 人心太浮躁,个人深度不够,只学习了一些皮毛,后面博客只求精,不求多: 希望能坚持下来.加油! 学习点: 实现效果 django与scrapy的创建 setting中对接的位置和代码段 scrapy_djangoitem使用 scrapy数据爬取保存部分 数据库设计以及问题部分 django配置 实现效果: django与scrapy

  • python Django编写接口并用Jmeter测试的方法

    一.环境准备 python3.6.7 Pycharm 二.创建项目 我这里是在Django项目中新建了个APP,目录结构如下图所示: 那么怎么在已有的Django项目中新建APP并进行配置呢: 2.1.新建app a.可以在终端输入命令:python manage.py startapp myapp(这里myapp是指你自己app的名称),如下图所示: b.也可以在pycharm中找到Tools-->Run manage.py Task, 在弹出的命令框中输入:startapp myapp(这里

  • Go语言学习函数+结构体+方法+接口

    目录 1. 函数 1.1 函数返回值 同一种类型返回值 带变量名的返回值 函数中的参数传递 函数变量 1.2 匿名函数——没有函数名字的函数 在定义时调用匿名函数 将匿名函数赋值给变量 匿名函数用作回调函数 可变参数——参数数量不固定的函数形式 1.3 闭包 1.4 defer语句 处理运行时发生的错误 1.5 宕机恢复(recover)——防止程序崩溃 2. 结构体 2.1 定义与给结构体赋值 3. 方法 3.1 结构体方法 3.2 接收器 指针接收器 非指针类型接收器 4. 接口 4.1 声

  • Go语言学习之结构体和方法使用详解

    目录 1. 结构体别名定义 2. 工厂模式 3. Tag 原信息 4. 匿名字段 5. 方法 1. 结构体别名定义 变量别名定义 package main import "fmt" type integer int func main() { //类型别名定义 var i integer = 1000 fmt.Printf("值: %d, 类型: %T\n", i, i) var j int = 100 j = int(i) //j和i不属于同一类型,需要转换 fm

  • 浅谈Go语言中的结构体struct & 接口Interface & 反射

    结构体struct struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套: go中的struct类型理解为类,可以定义方法,和函数定义有些许区别: struct类型是值类型. struct定义 type User struct { Name string Age int32 mess string } var user User var user1 *User = &User{} var user2 *User = new(User) struct使用 下面示例中user1和

  • Go语言中结构体方法副本传参与指针传参的区别介绍

    GO语言结构体方法跟结构体指针方法的区别 首先,我定了三个接口.一个结构和三个方法: type DeptModeA interface { Name() string SetName(name string) } type DeptModeB interface { Relocate(building string, floor uint8) } type Dept struct { name string building string floor uint8 Key string } fun

  • goalng 结构体 方法集 接口实例详解

    目录 一 前序 二 事出有因 errors.As 方法签名 三 结构体与实例的数据结构 1. 结构体类型 2. 实例 3 方法调用 3.1 方法表达式 3.2 值实例调用所有方法 3.3 指针实例调用所有方法 3.4 空指针无法调用值方法 四 接口 1 接口数据结构 2 接口赋值 值方法集 指针方法集 总结 一 前序 很多时候我们以为自己懂了,但内心深处却偶有困惑,知识是严谨的,偶有困惑就是不懂,很幸运通过大量代码的磨练,终于看清困惑,并弄懂了. 本篇包括结构体,类型, 及 接口相关知识,希望对

  • Go语言指针访问结构体的方法

    本文实例讲述了Go语言指针访问结构体的方法.分享给大家供大家参考.具体分析如下: Go有指针,但是没有指针运算. 结构体字段可以通过结构体指针来访问.通过指针间接的访问是透明的. 复制代码 代码如下: package main import "fmt" type Vertex struct {     X int     Y int } func main() {     p := Vertex{1, 2}     q := &p     q.X = 1e9     fmt.P

  • C语言示例讲解结构体的声明与初始化方法

    目录 一.结构体声明的结构 1.直接声明 2.使用typedef声明一个新的类型 3.不完全声明 二.结构体初始化 1.声明(同时定义)时直接赋值 2.定义时直接赋值 3.定义后赋值 4.指定初始化 一.结构体声明的结构 1.直接声明 struct tag { member-list: member-list: member-list: ... } variable-list; tag 是结构体类型的标签. member-list 结构体的元素定义,比如 int i; 或者 float f,或者

  • go语言数组及结构体继承和初始化示例解析

    目录 分类 数组 数组定义 结构体 结构体继承 结构体初始化 成员的操作 同名字段 其它匿名字段 非结构体类型 结构体指针类型 结构体字段实现接口 分类 类型 名称 长度 默认值 说明 pointer 指针   nil   array 数组   0   slice 切片   nil 引⽤类型 map 字典   nil 引⽤类型 struct 结构体       数组 如果要存储班级里所有学生的数学成绩,应该怎样存储呢?可能有同学说,通过定义变量来存储.但是,问题是班级有80个学生,那么要定义80

  • C语言中的结构体在Python中实现转换

    目录 struct介绍 struct中的常用接口 pack() unpack() fmt 示例 struct介绍 Python中提供了struct接口,用来处理类似C语言中的结构体. 处理的方式是将结构体表现位字符串,这个字符串其实就是结构体的一个个字节. struct中的常用接口 主要就是两个,pack()和unpack(). pack()就是将结构体转换成字符串(或者说字节序),unpack()则相反. pack() pack()函数的说明如下(来自Python 2.7.15 documen

  • C语言全面梳理结构体知识点

    目录 一.什么是结构体 二.结构体的定义 三.结构体变量的定义 四.结构体变量的初始化 五.结构体变量的赋值 六.引用结构体变量中的成员 七.结构体变量的传参问题 八.传输地址带来的问题 九.动态结构体数组 十.关键字typedef 十一.C++中的引用 一.什么是结构体 为了更好地模拟现实,需要把各种基本数据类型组合在一起构成一种新的复合数据类型,我们把这种自定义的数据类型称为结构体.结构体是程序员根据实际需求,把各种基本数据类型组合在一起构成的一种新的复合数据类型. 二.结构体的定义 结构体

随机推荐