interface作为struct field,谈谈golang结构体中的匿名接口

78次阅读

共计 2478 个字符,预计需要花费 7 分钟才能阅读完成。

golang 中通过组合 (composite) 实现类似继承 (extends) 和重写 (override) 的功能,大家可能平时用的比较多的是 struct 中匿名 struct 的写法,有没有见过 struct 中匿名接口 (anonymous interface) 的写法呢?
Interface 这个接口直接作为 struct 中的一个匿名字段,在标准库 sort 包中就有这种写法:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}

type reverse struct {
Interface
}
下面我们来看一个完整的例子,以下代码是从 sort 包提取出来的:
package main

import (
“fmt”
)

type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}

// Array 实现 Interface 接口
type Array []int

func (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 main

import (
“fmt”
)

type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}

type Array []int

func (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))
}

上面这个例子使用了匿名结构体的写法,和之前匿名接口的写法实现了同样的重写功能,甚至非常相似。但是仔细对比一下你就会发现匿名接口的优点,匿名接口的方式不依赖具体实现,可以对任意实现了该接口的类型进行重写。这在写一些公共库时会非常有用,如果你经常看一些库的源码,匿名接口的写法应该会很眼熟。
匿名接口还有一个作用就是对结构体添加一些约束,必须使用实现了该接口的类型来构造实例。结构体中可以包含一些其他的字段,而 interface 只有方法,没有 field。
package main

import (
“fmt”
“reflect”
“sort”
)

type Array1 []int

func (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 []int

func (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 field
Type 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))
}

正文完
 0