方法
func (s *Struct1)funcName(a,b int) (string,int) {
...
}
-
方法比函数多了一个接受者
- 方法作用于结构体或者自创类型
- 类似c++里的成员函数
- 方法作用于结构体或者自创类型
-
方法的声明和使用者可以不在一个文件中,但要在一个包中
-
两种类型的接收者:值接收者和指针接收者
- 使用值类型接收者定义的方法,在调用的时候,使用的其实是值的副本,所以对该值的任何操作,不会影响原来的变量
- 如果想要方法对接受者作出改变,那么就应该使用指针作为接受者 ,使用的是指针的副本
- 可以类比为recevier是方法的第一个参数
- 在使用的时候,值或者指针都可以直接调用,会自动根据声明取地址或者解引用
-
String方法
- print函数在打印自定义数据类型时会自动调用其String方法
func (t T) String() string
接口
- 如果一个类型实现了某一个接口的所有方法,则所有使用该接口的地方,都可以支持该类型的值
- 不要使用一个指针指向一个接口类型,因为接口本身已经是一个指针
- 接口调用时动态计算后的跳转调用,会导致CPU缓存失效和分支预测失败
- 接口变量初始化才有意义,默认为nil
- 空接口可以被任意类型实现
type empty interface{}
- 所以利用空接口可以实现泛型
- 编译器校验接口能否被赋值是比较二者方法集,而不是具体接口类型名
- 如果A接口的方法集是B的超集,则A的接口变量可以直接赋值给B
- 接口变量的底层实现
- 接口的值是一个两个字长度的数据结构
- 第一个字包含一个指向内部表结构的指针,这个内部表里存储接口类型,绑定的实例类型以及相关联的方法集
- 第二个字包含的是一个指向存储的实体类型值的指针
-
类型断言
- 语法:
i.(TypeName)
- i必须为接口变量
- 如果TypeName是具体类型,则为判断该接口绑定实例的类型是否是该具体类型
- 如果TypeName是借口类型,则会判断该接口绑定实例是否同时实现了该接口类型
type Element interface{ io.Reader } // element必须为借口类型 var element Element = 1 if value,ok := element.(int); ok { ... } if iface, ok := element.(io.Reader); ok { ... }
- 语法:
-
类型查询
- 在处理来自于外部的、类型未知的数据时,比如解析诸如 JSON 或 XML 编码的数据时会非常有用
- 语法:
switch v := i.(type){}
- i必须为接口变量
- 如果i没有绑定任何实例,则v值为nil
- case后面既可跟具体类型,也可跟接口类型
- 不能使用fallthrough
switch v := i.(type) {
case nil:
...
case string:
...
}
switch v:= i.(type) {
case io.Reader, io.Writer:
...
}
- 方法集
- 值类型变量的方法集只包含值接受者声明的方法
- 指针类型变量的方法集同时包含值和指针接受者声明的方法
// 指针声明的方法
func(u *User) notify() {
...
}
type notifier interface {
notify()
}
func sendNotify(n notifier) {
n.notify
}
var u User
// 下面的函数编译不通过
// 因为User类型只实现了指针方法集,不能传入值
sendNotify(n)
// 下面的函数通过
sendNotify(&n)
// 上面这样规定的原因是编译器不是总能自动获得一个值得地址,例如这个值是一个常数25
、、
-
不允许空接口切片作为参数
-
当参数是接口时,如何使用for range
-
判断一个切片是否包含一个元素
- Stack Overflow
- 如果是一个string切片,可以使用map替代,但是如果是一个自定义结构切片,无法使用map
-
接口型函数
-
通过接口依赖注入实现解耦
匿名字段
- 结构体中只有类型名而没有变量名的字段
- 于是该结构体继承了内嵌的匿名字段的内部字段和方法
- 以此可以模拟继承
- 可以直接调用内部类型的方法:
outer.innerfunc()
- 如果内部类型实现了某个接口,外部类型也自动实现了
- 如果外部类型有和内部类型同名的方法,那么会覆盖内部方法
- 此时如果要调用内部方法,就要显式地通过访问内部类型来访问
outer.inner.func()
- 从一个现有的非 interface 类型创建新类型时,并不会继承原有的方法
- 如果你需要使用原类型的方法,可将原类型以匿名字段的形式嵌到你定义的新 struct 中
// myMutex没有Lock()方法
type myMutex sync.Mutex
// myMutex有Lock()方法
type myLocker struct {
sync.Mutex
}