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)方法的优点 使程序变得更简短而 ...
随机推荐
- GPUImage API文档之GPUImageInput协议
GPUImageInput协议主要包含一些输入需要渲染目标的操作. - (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)t ...
- Solidworks直接打开SWB文件报错怎么办
- URAL 1224. Spiral (规律)
1224. Spiral Time limit: 1.0 second Memory limit: 64 MB A brand new sapper robot is able to neutrali ...
- Word2007的自动插入题注!
要在word中插入图片,是截屏获取的图片,还未保存成位图.要使用word2007的自动插入题注时,发现选项里面没有关于word图片的选项,2003里面有个word图片(差不多是这个叫法吧),在2003 ...
- Android 调用webservice并解析
这是调用webService的具体方法 private final static String nameSpace="http://tempuri.org/"; private f ...
- sqlalchemy 获取计数 count
from sqlalchemy import func message_count = self.db.query(func.count(Message.uid)).filter(Message.ui ...
- NameNode重新格式化以后DataNode不能启动
最近重新格式化NameNode以后,发现几个DataNode都不能启动了. 这是因为dfs.name.dir路径下面有一个current/VERSION文件,里面有一个clusterID,重新格式化以 ...
- python2.7 安装pypcap出错 pcap.h not found
python安装pypcap的时候出错 通过不断百度+google找到解决方案 https://segmentfault.com/q/1010000007273835/a-10200000072756 ...
- Hibernate学习笔记四:事务管理
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6768298.html 一:需要事务的Session操作 Session操作中,查询类操作是不需要事务就能生效 ...
- 写了一个简单的Linux Shell用来下载文件
#!/bin/sh ; i<; i=i+ )); do # 利用spider来探测请求的资源是否存在,并把请求的结果写入到一个文件 wget --spider --http-user=usern ...