变量和实例
空接口
4.1 基本概念
4.1.1 接口声明
接口定义大括号内可以是方法声明的集合,也可以嵌入另一个接口类型匿名字段,还可以是两者混合。
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
//如下三种声明时等价的,最终的展开模式都是第3种格式
type ReadWriter interface {
Reader
Writer
}
type ReadWriter interface {
Reader
Write(p []byte) (n int, err error)
}
type ReadWriter interface {
Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
}
方法声明
方法声明=方法名+方法签名
声明新接口类型的特点
1) 接口命名一般以 “er” 结尾
2)接口定义的内部方法声明不需要 func 引导
3) 在接口定义中,只有方法声明没有方法实现
4.1.2 接口初始化
接口只有被初始化为具体的类型才有意义。
实例赋值接口
接口变量赋值接口变量
file, _ := os.OpenFile("notes.txt", os.O)RDWR | os.O_CREATE, 0755)
var rw io.ReadWriter = file
// io.ReadWriter 接口可以直接赋值给 io.Writer接口变量
var w io.Writer = rw
4.1.3 接口方法调用
接口方法调用最终地址是在运行期决定的,将具体类型变量赋值给接口后,会使用具体类型的方法指针初始化接口变量,当调用接口变量的方法时,实际上是间接地调用实例的方法。
接口方法调用不是一种一直接的调用,有一定的运行时开销。
type Printer interface {
Print()
}
type S struct {}
func (s S) Print() {
println("print")
}
func main() {
var i Printer
// 没有初始化的接口调用其方法会产生panic
// i.Print()
// 必须初始化
i = S {}
i.Print()
}
4.1.4 接口的动态类型和静态类型
动态类型
静态类型
4.2 接口运算
基于接口编程是Go语言编程的基本思想
4.2.1 类型断言
接口断言的两种语法表现
o := i.(TypeName)
如果不满足程序会抛panic。例如:
type Inter interface {
Ping()
Pang()
}
type Anter interface {
Inter
String()
}
type St struct {
Name string
}
func (St) Ping() {
println("ping")
}
func (*St) Pang() {
println("pang")
}
func main() {
st := &St { "andes" }
var i interface {} = st
// 判断i绑定的实例是否实现了接口类型Inter
o := i.(Inter)
o.Ping()
o.Pang()
// 如下语句会引发panic, 因为 i 没有实现接口 Anter
// p := i.(Anter)
// p.String()
// 判断 i 绑定的实例是否就就是具体类型St
s := i.(*St)
fmt.Printf("%s", s.Name)
}
comma, ok 表达式模式如下:
if o, ok := i.(TypeName)l ok {
}
4.2.2 类型查询
接口查询有两层语义,
1是查询一个接口变量底层绑定的底层变量的具体类型是什么
2是查询接口变量绑定的底层变量是否还实现其他接口
switcg v := i.(type)
case type1:
xxxx
case type2:
xxxx
default:
xxxx
1) i 必须是接口类型
var i io.Reader
switch v := i.(type) { // 此处i是未初始化的接口变量,所以v为nil
case nil:
fmt.Printf("%T\n", v) // nil
default:
fmt.Printf("default")
}
2)case 字句后面可以跟非接口类型名,也可以跟接口类型名,匹配是按照case子句的顺序进行的。
4.2.3 接口优点和使用形式
接口优点
1)解耦
2)实现泛型
接口使用形式
1)作为结构内嵌字段
2)作为函数或者方法的形参
3)作为函数或方法的返回值
4)作为其他接口定义的嵌入字段
4.3 空接口
没有任何方法的接口 ,我们称为空接口。类似java中的Object
4.3.2 空接口的用途
空接口和泛型
Go语言没有泛型,如果一个函数需要接口任意类型的参数 ,则参数类型可以使用空接口类型
// 典型的就是fmt标准表 里面的print函数
func Fprint(w io.Writer, a ...interface{}) (n int, err error)
空接口和反射
4.3.3 空接口和nil
空接口不是真的为空,接口有类型和值两个概念。
type Inter interface {
Ping()
Pang()
}
type St struct {}
func (St) Ping () {
println("ping")
}
func (*St) Pang() {
println("pang")
}
func main() {
var st *St = nil
var it Inter = st
fmt.Printf("%p\n", st)
fmt.Printf("%p\n", it)
if it !=nil {
it.Pang()
// 下面的语句会导致panic
// 方法转换为函数调用,第一个参数是St类型,由于*St是nil, 无法获取指针所指的对象值,所以导致panic (这里没有明白是啥,等我先把这个搞清楚明白再继续往下写。134)
// it.Ping()
}
}
4.4 接口内部实现
4.4.1 数据结构
4.4.2 接口调用过程分析
4.4.3 接口调用的代价
4.4.4 空接口数据结构