golang学习笔记---函数、方法和接口
函数:对应操作序列,是程序的基本组成元素。
函数有具名和匿名之分:
具名函数一般对应于包级的函数,是匿名函数的一种特例,当匿名函数引用了外部
作用域中的变量时就成了闭包函数,闭包函数是函数式编程语言的核心。方法是绑
定到一个具体类型的特殊函数,Go语言中的方法是依托于类型的,必须在编译时静
态绑定
接口:定义了方法的集合,这些方法依托于运行时的接口对象,因此接口对
应的方法是在运行时动态绑定的。
Go程序函数启动顺序的示意图:

要注意的是,在 main.main 函数执行之前所有代码都运行在同一个goroutine,也
就是程序的主系统线程中。
因此,如果某个 init 函数内部用go关键字启动了新的
goroutine的话,新的goroutine只有在进入 main.main 函数之后才可能被执行到。
函数
在Go语言中,函数是第一类对象,我们可以将函数保持到变量中。函数主要有具名
和匿名之分,包级函数一般都是具名函数,具名函数是匿名函数的一种特例
// 具名函数
func Add(a, b int) int {
return a+b
}
// 匿名函数
var Add = func(a, b int) int {
return a+b
}
Go语言中的函数可以有多个参数和多个返回值,参数和返回值都是以传值的方式和
被调用者交换数据。在语法上,函数还支持可变数量的参数,可变数量的参数必须
是最后出现的参数,可变数量的参数其实是一个切片类型的参数。
// 多个参数和多个返回值
func Swap(a, b int) (int, int) {
return b, a
}
// 可变数量的参数
// more 对应 []int 切片类型
func Sum(a int, more ...int) int {
for _, v := range more {
a += v
}
return a
}
当可变参数是一个空接口类型时,调用者是否解包可变参数会导致不同的结果:
package main
import (
"fmt"
)
func main() {
var a = []interface{}{123, "abc"}
Print(a...) // 123 abc
Print(a) // [123 abc]
}
func Print(a ...interface{}) {
fmt.Println(a...)
}
第一个 Print 调用时传入的参数是 a... ,等价于直接调用 Print(123,
"abc") 。第二个 Print 调用传入的是未解包的 a ,等价于直接调
用 Print([]interface{}{123, "abc"}) 。
‘…’ 其实是go的一种语法糖。
它的第一个用法主要是用于函数有多个不定参数的情况,可以接受多个不确定数量的参数。
第二个用法是slice可以被打散进行传递。
不仅函数的参数可以有名字,也可以给函数的返回值命名:
func Find(m map[int]int, key int) (value int, ok bool) {
value, ok = m[key]
return
}
如果返回值命名了,可以通过名字来修改返回值,也可以通过 defer 语句
在 return 语句之后修改返回值:
func Inc() (v int) {
defer func(){ v++ } ()
return 42
}
其中 defer 语句延迟执行了一个匿名函数,因为这个匿名函数捕获了外部函数的
局部变量 v ,这种函数我们一般叫闭包。闭包对捕获的外部变量并不是传值方式
访问,而是以引用的方式访问。
闭包的这种引用方式访问外部变量的行为可能会导致一些隐含的问题:
package main import (
"fmt"
) func main() {
for i := 0; i < 3; i++ {
defer func() { fmt.Println(i) }()
}
}
// Output:
// 3
// 3
// 3
因为是闭包,在 for 迭代语句中,每个 defer 语句延迟执行的函数引用的都是
同一个 i 迭代变量,在循环结束后这个变量的值为3,因此最终输出的都是3。
修复的思路是在每轮迭代中为每个 defer 函数生成独有的变量。可以用下面两种
方式:
package main import (
"fmt"
) func main() {
for i := 0; i < 3; i++ {
i := i // 定义一个循环体内局部变量i
defer func() { fmt.Println(i) }()
}
}
package main import (
"fmt"
) func main() {
for i := 0; i < 3; i++ {
// 通过函数传入i
// defer 语句会马上对调用参数求值
defer func(i int) { fmt.Println(i) }(i)
}
}
不过一般来说,在 for 循环内部执行 defer 语句并不是一个好
的习惯,此处仅为示例,不建议使用。
每个goroutine刚启动时只会分配
很小的栈(4或8KB,具体依赖实现),根据需要动态调整栈的大小,栈最大可以
达到GB级(依赖具体实现,在目前的实现中,32位体系结构为250MB,64位体系结构为1GB)
Go语言中指针不再是固定不变的了(因此
不能随意将指针保持到数值变量中,Go语言的地址也不能随意保存到不在GC控制
的环境中,因此使用CGO时不能在C语言中长期持有Go语言对象的地址
方法
Go语言的方法关联到类型的,这样可以在编译阶段完成方法的静态绑定。
我们可以给任何自
定义类型添加一个或多个方法。每种类型对应的方法必须和类型的定义在同一个包
中,因此是无法给 int 这类内置类型添加方法的(因为方法的定义和类型的定义
不在一个包中)。对于给定的类型,每个方法的名字必须是唯一的,同时方法和函
数一样也不支持重载。
方法是由函数演变而来,只是将函数的第一个对象参数移动到了函数名前面了而
已。因此我们依然可以按照原始的过程式思维来使用方法。通过叫方法表达式的特
性可以将方法还原为普通类型的函数:
Go的接口类型是对其它类型行为的抽象和概括,
因为接口类型不会和特定的实现细
节绑定在一起,通过这种抽象的方式我们可以让对象更加灵活和更具有适应能力。
很多面向对象的语言都有相似的接口概念,但Go语言中接口类型的独特之处在于它
是满足隐式实现的鸭子类型。所谓鸭子类型说的是:只要走起路来像鸭子、叫起来
也像鸭子,那么就可以把它当作鸭子
fmt.Fprintf 函数的签
名如下:
func Fprintf(w io.Writer, format string, args ...interface{}) (int, error)
其中 io.Writer 用于输出的接口, error 是内置的错误接口,
它们的定义如
下:
type io.Writer interface {
Write(p []byte) (n int, err error)
}
type error interface {
Error() string
}
我们可以通过定制自己的输出对象,将每个字符转为大写字符后输出
package main import (
"bytes"
"fmt"
"io"
"os"
) type UpperWriter struct {
io.Writer
} func (p *UpperWriter) Write(data []byte) (n int, err error) {
return p.Writer.Write(bytes.ToUpper(data))
}
func main() {
fmt.Fprintln(&UpperWriter{os.Stdout}, "hello, world")
}
golang学习笔记---函数、方法和接口的更多相关文章
- golang学习笔记--函数和方法
在go中,函数类型是一等类型,这意味着可以吧函数当做一个值来传递和使用. func divide(dividend int,divisor int)(int,error){ //省略部分代码 } 参数 ...
- golang学习笔记13 Golang 类型转换整理 go语言string、int、int64、float64、complex 互相转换
golang学习笔记13 Golang 类型转换整理 go语言string.int.int64.float64.complex 互相转换 #string到intint,err:=strconv.Ato ...
- golang学习笔记8 beego参数配置 打包linux命令
golang学习笔记8 beego参数配置 打包linux命令 参数配置 - beego: 简约 & 强大并存的 Go 应用框架https://beego.me/docs/mvc/contro ...
- golang学习笔记7 使用beego swagger 实现API自动化文档
golang学习笔记7 使用beego swagger 实现API自动化文档 API 自动化文档 - beego: 简约 & 强大并存的 Go 应用框架https://beego.me/doc ...
- golang学习笔记19 用Golang实现以太坊代币转账
golang学习笔记19 用Golang实现以太坊代币转账 在以太坊区块链中,我们称代币为Token,是以太坊区块链中每个人都可以任意发行的数字资产.并且它必须是遵循erc20标准的,至于erc20标 ...
- golang学习笔记14 golang substring 截取字符串
golang学习笔记14 golang substring 截取字符串golang 没有java那样的substring函数,但支持直接根据 index 截取字符串mystr := "hel ...
- golang学习笔记9 beego nginx 部署 nginx 反向代理 golang web
golang学习笔记9 beego nginx 部署 nginx 反向代理 golang web Nginx 部署 - beego: 简约 & 强大并存的 Go 应用框架https://bee ...
- golang学习笔记6 beego项目路由设置
golang学习笔记5 beego项目路由设置 前面我们已经创建了 beego 项目,而且我们也看到它已经运行起来了,那么是如何运行起来的呢?让我们从入口文件先分析起来吧: package main ...
- Java学习笔记之---方法和数组
Java学习笔记之---方法与数组 (一)方法 (1)什么是方法? 方法是解决一类问题的步骤的有序组合 方法包含于类或对象中 方法在程序中被创建,在其他地方被引用 (2)方法的优点 使程序变得更简短而 ...
随机推荐
- log4j和web.xml配置webAppRootKey 的问题(一个tomcat下部署多个应用)
转自:http://blog.csdn.net/arvin_qx/article/details/6829873 在tomcat下部署两个或多个项目时,web.xml文件中最好定义webAppRoot ...
- 坑人无数的Redis面试题
https://baijiahao.baidu.com/s?id=1588454565071211950&wfr=spider&for=pc 坑人无数的Redis面试题
- 在Ubuntu系统上搭建Hadoop 2.x(2.6.2)
官方的中文版的Hadoop快速入门教程已经是很老的版本了,新版的Hadoop目录结构发生了变化,因此一些配置文件的位置也略微调整了,例如新版的hadoop中找不到快速入门中提到的conf目录,另外,网 ...
- Android 在闹钟开机时,如何解决开机动画没有播完就进入Launcher M
前言 欢迎大家我分享和推荐好用的代码段~~ 声明 欢迎转载,但请保留文章原始出处: CSDN:http://www.csdn.net ...
- java相关知识集锦
java语言基础知识: Java8 Stream语法详解 不用循环 java 8系列之Stream的基本语法详解 java8 stream filter等功能代替for Java中try catch ...
- string format 格式化小数位
String具体的格式化数据的方法 int a = 12345678;格式为sring输出Label1.Text = string.Format("asdfadsf{0}adsfasdf&q ...
- SpringMVC+Spring+mybatis项目从零开始--Spring mybatis mysql配置实现
上一章我们把SSM项目结构已搭建(SSM框架web项目从零开始--分布式项目结构搭建)完毕,本章将实现Spring,mybatis,mysql等相关配置. 1. 外部架包依赖引入 外部依赖包引入 ...
- Linux账号管理与ACL权限设置
1:UID和GID 用户ID:在/etc/passwd中 群组ID:在/etc/group中 2:有效群组与初始群组 初始群组:/etc/passwd文件里面的GID 有效群组: groups #查看 ...
- Spring学习笔记五:Spring进行事务管理
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6776256.html 事务管理主要负责对持久化方法进行统一的提交或回滚,Spring进行事务管理即我们无需在 ...
- 《Cocos2d-JS 开发之旅》即将发行,Cocos2d-x联合创始人林顺作序力荐
受电子工业出版社邀请,经过半年多的酝酿,<Cocos2d-JS 开发之旅>(作者:郑高强)已经出版了,本书详细讲述如何使用Cocos2d-JS制作HTML5游戏和原生手机游戏,另外还有部分 ...