• 周二. 3月 5th, 2024

    GO语言接口

    root

    10月 15, 2019 #Golang, #Go学习

    7.1 定义

    Go接口实现机制很简洁,只要目标类型方法集内包含接口声明的全部方法,就被视为实现了该接口,无须做显式声明。目标类型可以实现多个接口
    从内部实现来看,接口自身也是一种结构类型,只是编译器会对其做出很多限制
    type iface struct {
        tab *itab
        data unsafe.Pointer
    }
    
    • 不能有字段
    • 不能定义自己的方法
    • 只能声明方法,不能实现
    • 可嵌入其他接口类型
    接口通常以er作为名称后缀,方法名是声明组成部分,但参数名可不同或省略
    type tester interface {
    	test()
    	string() string
    }
    type data struct {
    
    }
    func (*data) test() {}
    func (data) string() string {
    	return ""
    }
    
    func main() {
    	var d data
    	//var t tester = d	//错误: data does not implement tester(test method has pointer receiver)
    
    	var t tester = &d
    	t.test()
    	println(t.string())
    }
    
    编译器根据方法集来判断是否实现了接口,显然在上例中只有*data才符合tester的要求
    空接口(interface{}), 没有任何方法声明。它的用途类似面向对象里的Object.可被赋值为任何类型的对象。
    接口变量默认值是nil. 如果实现接口的类型支持,可做相等运算
    func main() {
    	var t1, t2 interface{}
    	println(t1 == nil, t1 == t2)
    
    	t1, t2 = 100, 100
    	println(t1 == t2)
    
    	t1, t2 = map[string]int{}, map[string]int{}
    	println(t1 == t2)
    }
    
    可以像匿名字段那样,嵌入其他接口。目标类型方法集中必须拥有包含嵌入接口方法在内的全部方法才算实现了该接口。
    type stringer interface {
    	string() string
    }
    type tester interface {
    	stringer		//嵌入其他接口
    	test()
    }
    type data struct {}
    func (*data) test() {
    	println("this is test()")
    }
    func (data) string() string {
    	return "this is string()"
    }
    func main() {
    	var d data
    	var t tester = &d
    	t.test()
    	println(t.string())
    }
    
    超集接口变量可隐式转换为子集,反过来不行
    支持匿名接口类型,可直接用于变量定义,或作为结构字段类型
    type data struct {
    
    }
    func(data) string() string {
    	return "this is string()"
    }
    type node struct {
    	data interface{			//匿名接口类型
    		string() string
    	}
    }
    func main() {
    	var t interface{	//定义匿名接口变量
    		string() string
    	}  = data{}
    	n := node{
    		data:t,
    	}
    	println(n.data.string())
    }
    

    7.2执行机制(看不懂暂时不写)

    接口使用一个名为itab的结构存储运行期所需要的相关类型信息。
    利用调试器,我们可查看这些结构存储的具体内容

    7.3 类型转换

    类型推断可将接口变量还原为原始类型,或用来判断是否实现了某个更具体的接口类型
    type data int
    func (d data) String() string {
    	return fmt.Sprintf("data:%d", d)
    }
    func main() {
    	var d data = 15
    	var x interface{} = d
    	if n, ok := x.(fmt.Stringer); ok { //转换为更具体的接口类型
    		fmt.Println(n)
    	}
    	if d2, ok := x.(data); ok {	//转换回原始类型
    		fmt.Println(d2)
    	}
    	e := x.(error)	//错误:main.data is not error
    	fmt.Println(e)
    }
    
    使用ok-idiom模式,即便转换失败也不会引发panic。还可用switch语句在多种类型间做出推断匹配,这样空接口就有更多发挥空间。
    func main() {
    	var x interface{} = func(x int) string {
    		return fmt.Sprintf("d:%d", x)
    	}
    	switch v := x.(type) {
    	case nil:
    		println("nil")
    	case *int:
    		println(*v)
    	case func(int) string:
    		println(v(100))
    	case fmt.Stringer:
    		fmt.Println(v)
    	default:
    		println("unknown")
    	}
    }
    

    7.4 技巧

    让编译器检查,确保类型实现了指定接口
    type x int
    func init() {           //包初始化函数
        var _ fmt.Stringer = x(0)
    }
    /*
    cannot use x(0) (type x ) as type fmt.Stringer in assignment
    x does not implement fmt.Stringer (missing String method)
    */
    
    定义函数类型,让相同签名的函数自动实现某个接口
    type FuncString func() string
    
    func( f FuncString) String() string {
    	return f()
    }
    
    func main() {
    	var t fmt.Stringer = FuncString(func() string {	//转换类型,使其实现Stringer接口
    		return "hello World";
    	})
    	fmt.Println(t)
    }

    root

    发表回复