Golang中的接口:代表一种调用契约,是多个方法声明的集合
接口类似于C++中的纯虚函数,只需要关注的是做什么,而不去关注具体怎么做由谁来做(一种面向对象的思想)。
接口相比于C++中的纯虚函数更加灵活,只需要实现接口的全部方法就可以进行使用了。
接口的优点:
解除类型依赖
屏蔽内部结构和实现细节
有助于程序的扩展
使用接口的注意:由于接口的内部实现机制会有运行期开销
对于相同的包,或不频繁变化的内部模块之间,是不建议使用接口的
常见的使用场景,是包对外提供访问,或预留扩展空间
接口是对具体事物的抽象,是将一个具体的事物抽象成一个接口。
比如植物:就是所有具有根,茎,叶等具体的事物的抽象的概念。
编译器对接口有限制:
不能有字段
不能定义自己的方法
只能声明方法,不能实现
可嵌入其他接口类型
接口的使用:
package main
import "fmt"
type tester interface {
test()
string() string
}
type data struct{}
func (*data) test() {
fmt.Println("world")
}
func (data) string() string {
return "hello"
}
func main() {
var d data
var t tester = &d
fmt.Println(t.string())
t.test()
}
如果是一个接口没有任何方法声明,就是一个空接口类型。接口变量默认值是nil。
在接口中可以嵌入其他接口,如果有类型使用这个接口就必须实现接口中的全部方法(包括嵌入的接口方法),不能嵌入自身或循环嵌入
package main
import "fmt"
type User interface {
Name() string
Age()
}
type People interface {
User
Tell()
}
type student struct{}
func (*student) Tell() {
fmt.Println("12345")
}
func (*student) Name() string {
return "name"
}
func (*student) Age() {
fmt.Println("100")
}
func main() {
var s student
var p People = &s
fmt.Println(p.Name())
p.Age()
p.Tell()
}
匿名接口的实现:
package main
import "fmt"
type data struct{}
func (data) string() string {
return "hello"
}
type node struct {
data interface { //匿名接口类型
string() string
}
}
func main() {
var t interface { //定义匿名接口变量
string() string
}
t = data{}
n := node{
data: t,
}
fmt.Println(n.data.string())
}
接口的执行机制
接口使用一个名为itab的结构体存储运行期所需的相关类型信息
type iface struct{
tab *itab //类型信息
data unsafe.Pointer //实际对象指针
}
type itab struct {
inter *interfacetype //接口类型
_type *_type //实际对象类型
fun [1]uintptr //实际队象方法地址
}
接口的类型转换:
接口可以通过类型推断将接口变量还原为原始类型或者用来判断是否实现看某个更具体的接口类型
package main
import "fmt"
type data int
func (d data) String() string {
return fmt.Sprintf("data:%d", d)
}
func main() {
var d data = 10
var x interface{} = d
if n, ok := x.(fmt.Stringer); ok { //转换为更具体的接口类型
fmt.Println(n)
}
if b, ok := x.(data); ok { //转为原始类型
fmt.Println(b)
}
e := x.(error) //将会报错
fmt.Println(e)
}
使用类型推断即使转换失败也不会引发panic