文章目录
- 结构体嵌套接口
- 结构体嵌套结构体
- 结构体实现接口
- 接口嵌套接口
- 结构体嵌套接口
- 例子1
- 例子2
- 总结
- 参考资料
结构体嵌套接口
今天看k8s client-go源码时发现,结构体嵌套接口的现象。
// deployments implements DeploymentInterface
type deployments struct {client rest.Interfacens string
}
之前的学习都是以下情况,
结构体嵌套结构体
type Address struct {City stringState string
}type Person struct {Name stringAge intAddress // 结构体嵌套
}
通过组合的方式,增强了结构体的功能
结构体实现接口
package mainimport "fmt"type Writer interface {Write(string)
}type ConsoleWriter struct{}func (cw ConsoleWriter) Write(data string) {fmt.Println("Writing:", data)
}type Logger struct {Writer // 接口嵌入
}func main() {logger := Logger{Writer: ConsoleWriter{}}logger.Write("Hello, world!")
}
最常见的情况,相当了实现了一个接口的对象
接口嵌套接口
type Name interface {WriteName(name string)
}
type Number interface {WriteNumber(number string)
}
type Person interface {NameNumberHello()
}
type person struct {name stringnumber string
}
func (p *person) WriteName(name string) {p.name = name
}
func (p *person) WriteNumber(number string) {p.number = number
}
func (p person) Hello() {fmt.Println("hello")
}
func main() {p := person{}fmt.Println("Person:", p.name, p.number)p.WriteName("DoveOne")p.WriteNumber("1")p.Hello()fmt.Println("Person:", p.name, p.number)
}
增强了接口的方法集
结构体嵌套接口
例子1
package mainimport "fmt"type HelloWorld interface {Hello()
}type Person1 struct {Name stringNumber stringHelloWorld //匿名字段
}type Person2 struct {Name stringNumber stringtag HelloWorld //非匿名字段
}type hello struct {
}func (h hello) Hello() {fmt.Println("hello")
}func main() {h := hello{}p1 := &Person1{"DoveOne", "1", h}p1.Hello()p1.HelloWorld.Hello()p2 := &Person2{"DoveOne", "1", h}p2.tag.Hello() //非匿名字段必须指定字段名才能引用字段的方法
}
输出
hello
hello
hello
例子2
package mainimport ("fmt"
)type Interface interface {Len() intLess(i, j int) boolSwap(i, j int)
}// Array 实现Interface接口
type Array []intfunc (arr Array) Len() int {return len(arr)
}func (arr Array) Less(i, j int) bool {return arr[i] < arr[j]
}func (arr Array) Swap(i, j int) {arr[i], arr[j] = arr[j], arr[i]
}// 匿名接口(anonymous interface)
type reverse struct {Interface
}// 重写(override)
func (r reverse) Less(i, j int) bool {return r.Interface.Less(j, i)
}// 构造reverse Interface
func Reverse(data Interface) Interface {return &reverse{data}
}func main() {arr := Array{1, 2, 3}rarr := Reverse(arr)fmt.Println(arr.Less(0,1))fmt.Println(rarr.Less(0,1))
}
sort包中这么写的目的是为了重写Interface的Less方法,并有效利用了原始的Less方法;通过Reverse可以从Interface构造出一个反向的Interface。go语言利用组合的特性,寥寥几行代码就实现了重写。
对比一下传统的组合匿名结构体实现重写的写法,或许可以更好的帮助我们了解匿名接口的优点:
package mainimport ("fmt"
)type Interface interface {Len() intLess(i, j int) boolSwap(i, j int)
}type Array []intfunc (arr Array) Len() int {return len(arr)
}func (arr Array) Less(i, j int) bool {return arr[i] < arr[j]
}func (arr Array) Swap(i, j int) {arr[i], arr[j] = arr[j], arr[i]
}// 匿名struct
type reverse struct {Array
}// 重写
func (r reverse) Less(i, j int) bool {return r.Array.Less(j, i)
}// 构造reverse Interface
func Reverse(data Array) Interface {return &reverse{data}
}func main() {arr := Array{1, 2, 3}rarr := Reverse(arr)fmt.Println(arr.Less(0, 1))fmt.Println(rarr.Less(0, 1))
}
上面这个例子使用了匿名结构体的写法,和之前匿名接口的写法实现了同样的重写功能,甚至非常相似。但是仔细对比一下你就会发现匿名接口的优点,匿名接口的方式不依赖具体实现,可以对任意实现了该接口的类型进行重写。这在写一些公共库时会非常有用,如果你经常看一些库的源码,匿名接口的写法应该会很眼熟。
采用传统的组合匿名结构体方式,代码中的reverse 结构体与Array耦合
type reverse struct {Array }
采用匿名接口(anonymous interface)的方式,可以对任意实现了该接口的类型进行重写
type reverse struct {Interface }
匿名接口还有一个作用就是对结构体添加一些约束,必须使用实现了该接口的类型来构造实例。结构体中可以包含一些其他的字段,而interface只有方法,没有field。
package mainimport ("fmt""reflect""sort"
)type Array1 []intfunc (arr Array1) Len() int {return len(arr)
}func (arr Array1) Less(i, j int) bool {return arr[i] < arr[j]
}func (arr Array1) Swap(i, j int) {arr[i], arr[j] = arr[j], arr[i]
}type Array2 []intfunc (arr Array2) Len() int {return len(arr)
}func (arr Array2) Less(i, j int) bool {return arr[i] < arr[j]
}func (arr Array2) Swap(i, j int) {arr[i], arr[j] = arr[j], arr[i]
}type Sortable struct {sort.Interface// other fieldType string
}func NewSortable(i sort.Interface) Sortable {t := reflect.TypeOf(i).String()return Sortable{Interface: i,Type: t,}
}func DoSomething(s Sortable) {fmt.Println(s.Type)fmt.Println(s.Len())fmt.Println(s.Less(0, 1))
}func main() {arr1 := Array1{1, 2, 3}arr2 := Array2{3, 2, 1, 0}DoSomething(NewSortable(arr1))DoSomething(NewSortable(arr2))
}
总结
在Go语言中,结构体嵌入接口(embedding an interface in a struct)是一种将接口作为结构体字段的方式。但是,这里的“嵌入”与传统的字段嵌入稍有不同,因为接口本身不包含任何数据,只包含一组方法的签名。
当你在结构体中嵌入一个接口时,你实际上是在声明该结构体必须实现该接口的所有方法。这并不是说接口被“嵌入”到结构体中,而是说结构体承诺它将满足该接口的方法集。
参考资料
golang中结构体嵌套接口
Go语言结构体进阶
无风—interface 作为 struct field,谈谈 Golang 结构体中的匿名接口