这篇讲的非常重要,涉及到函数、方法和接口。在实际项目中用到的非常多,注意go语言中的接口是没有继承关系的,但是可以利用接口嵌套的方式实现接口继承的功能。
1.函数function
函数function
知识要点:
- 1、Go函数不支持嵌套、重载和默认参数;
- 2、但支持一下特性:
- 1、无须声明原型、不定长度变参、多返回值、命令返回值参数;
- 2、匿名函数、闭包
- 3、定义函数使用关键字
func
,且左大括号不能另起一行,函数也可以作为一种类型使用。
参数为不定长参数,相当于传递一个slice,如果修改参数slice中值会影响原slice的值吗?
package main
import "fmt"
func main() {
a,b :=1,2
B(a,b)
fmt.Println(a,b)
}
//不定长变参的使用方法,注意这里的s相当于一个slice
//但是如果修改slice中的值会怎么呢?
func B(s ...int) {
s[0]=3
s[1]=4
fmt.Println(s)
}

注意这个传递的slice是一个值传递(slice拷贝),不会影响原slice的值。
如果传递一个slice又会怎么样呢?(内存地址的拷贝)
package main
import "fmt"
func main() {
s1 :=[]int{1,2,3,4}
B(s1)
fmt.Println(s1)
}
//传递一个slice,这里的拷贝是内存地址,本质还是一个值传递
func B(s []int) {
s[0]=5
s[1]=6
s[2]=7
s[3]=8
fmt.Println(s)
}

指针传递(引用传递)
package main
import "fmt"
func main() {
a:=1
B(&a)
fmt.Println(a)
}
//传递一个指针
func B(a *int) {
*a =5
fmt.Println(*a)
}
函数本身传递
package main
import "fmt"
func main() {
//将B函数赋值给b,然后调用b
b:=B
b()
}
func B() {
fmt.Println("Func A")
}

匿名函数传递
package main
import "fmt"
func main() {
//匿名函数的调用
b:=func () {
fmt.Println("Func A")
}
b()
}
闭包(closure)
package main
import "fmt"
func main() {
f:=closure(10)
fmt.Println(f(1))
fmt.Println(f(2))
}
func closure(x int) func(int) int {
fmt.Printf("%p\n",&x)
return func(y int) int {
fmt.Printf("%p\n",&x)
return x+y
}
}

后面一篇博客会详细讲到闭包的作用。
2.方法method
方法method
的使用注意事项:
- 1、Go中虽没有
class
,但依旧有method
;通过显式说明Receiver
来实现与某个类型的组合;只能为同一个包中定义类型定义方法。 - 2、
Receiver
可以是类型的值或者指针; - 3、不存在方法重载问题;
- 4、可以使用值或指针来调用方法,编译器会自动完成转换;
- 5、从某种意义上来说,方法是函数的语法糖,因为
Receiver
其实就是方法所接收的第一个参数(Method Value与); - 6、如果外部结构和嵌入结构存在同名方法,则优先调用外部结构的方法;
- 7、类型别名不会拥有底层类型所附带的方法;
- 8、方法可以调用结构中的非公开字段。
语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·兰丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。
举例来说,许多程序语言提供专门的语法来对数组中的元素进行引用和更新。从理论上来讲,一个数组元素的引用涉及到两个参数:数组和下标向量,比如这样的表达式,get_array(Array, vector(i, j))。然而,许多语言支持这样直接引用 Array[i, j]。同理,数组元素的更新涉及到三个参数,set_array(Array, vector(i, j), value),但是很多语言提供这样直接赋值,Array[i, j] = value。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
//注意方法的定义,这里使用的指针传递参数
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
//初始化结构体
v := &Vertex{3, 4}
//利用指针调用方法
fmt.Println(v.Abs())
}
3.接口interface
接口interface
注意事项:
- 1、接口是一个或多个方法签名的集合;
- 2、只要某个类型拥有该接口的所有方法签名,即算实现该接口,无需显示声明实现了哪个接口,这称为
Structural Typing
; - 3、接口只有方法声明,没有实现,没有数据字段;
- 4、接口可以匿名嵌入其他接口,或嵌入到结构中;
- 5、将对象赋值给接口时,会发生拷贝,而接口内存存储的是指向这个复制品的指针,既无法修改复制品的状态,也无法获取指针;
- 6、只有当接口存储的类型和对象都为
nil
时,接口才等于nil
; - 7、接口调用不会做
receiver
的自动转换;接口同样支持匿名字段方法;接口也可实现类似OOP中的多态;空接口可以作为任何类型数据的容器。
接口的定义
package main
import "fmt"
//声明一个接口,里面有两个方法:Name和Connect方法
type USB interface {
Name() string
Connect()
}
//连接器结构体,这个类型实现了USB接口中所有方法(Name和Connect),所以PhoneConnector叫做实现了USB接口
type PhoneConnector struct {
name string
}
func (pc PhoneConnector) Name() string {
return pc.name
}
func (pc PhoneConnector) Connect() {
fmt.Println("Connect",pc.name)
}
func main() {
//定义USB接口类型的变量
//var a USB
//初始化接口
a:=PhoneConnector{"华为连接器"}
a.Connect()
Disconnect(a)
}
func Disconnect(usb USB) {
fmt.Println("Disconnect")
}

嵌入接口
将上面改为嵌入接口格式为:
//声明一个接口,里面有一个Name()方法和Connecter接口
type USB interface {
Name() string
//将Connecter接口嵌入到USB接口中
Connecter
}
type Connecter interface {
Connect()
}
如何判断interface变量存储的是哪种类型(类型断言)
一个interface被多种类型实现时,有时候我们需要区分interface的变量究竟存储哪种类型的值,go可以使用comma,ok
的形式做区分value,ok:=em.(T)
:em
是interface
类型的变量,T
代表要断言的类型,value
是interface
变量存储的值,ok
是bool
类型标识是否为该断言的类型T
。
//下面判断PhoneConnector是不是USB类型的
func Disconnect(usb USB) {
if pc,ok :=usb.(PhoneConnector);ok{
fmt.Println("Disconnect",pc.name)
return
}
fmt.Println("Unknown devive")
}
空接口
interface{}
是一个空的 interface 类型,根据前文的定义:一个类型如果实现了一个 interface 的所有方法就说该类型实现了这个 interface,空的 interface 没有方法,所以可以认为所有的类型都实现了 interface{}
。如果定义一个函数参数是 interface{}
类型,这个函数应该可以接受任何类型作为它的参数。

接口转换
接口只能有超接口转换子接口,相反则不行。

//将USB接口转换为Connecter接口
pc:=PhoneConnector{"华为连接器"}
var a Connecter
//转换动作
a=Connecter(pc)
a.Connect()
修改接口中的值,发生的是拷贝动作,不影响原来值
func main() {
//将USB接口转换为Connecter接口
pc:=PhoneConnector{"华为连接器"}
var a Connecter
a=Connecter(pc)
a.Connect()
//然后修改pc的name字段
pc.name="pc"
fmt.Println(pc.name)
a.Connect()
}

网友评论