golang interface学习

interface

概念

  • go中的接口使用隐式实现的
  • LSP里式替换-一个类型可以自由的使用另一个满足相同接口的类型来进行替换
  • 接口类型具体描述了一系列方法的集合,一个实现了这些方法的具体类型时这个接口类型的实例
  • go标准库存在大量的单方法接口的命名习惯

    1
    2
    3
    4
    5
    6
    7
    8
    package io
    type Reader interface {
    Read(p []byte) (n int, err error)
    }
    type Closer interface {
    Close() error
    }
  • 接口内嵌,关注的是内嵌的方法,和组合的设计模式思想类似

    1
    2
    3
    4
    5
    type ReadWriterCloser interface {
    Reader
    Writer
    Closer
    }
  • 一个类型持有一个方法,对于一个具体类型T,其一些方法的接收者可以是T也可以是T的指针,是因为比那一起隐式的获取了变量的地址。但是实际上T类型的值是不拥有所有*T指针的方法,这样就可能其实现更少的接口

    • IntSet类型的String方法的接收者是一个指针类型,则不能在一个不能寻址的IntSet值上调用这个方法

      1
      2
      3
      type IntSet struct {/* ... */}
      func (*IntSet) String() string
      var _ = IntSet{}.String() // compile error : String requires *IntSet receiver
    • 但是可以在一个IntSet值上调用这个方法

      1
      2
      var s IntSet
      var _ = s.String() // ok
    • 同时只有IntSet类型有String类型,也只有IntSet类型实现了fmt.Stringer接口

  • 接口类型封装和隐藏了具体类型和它的值,及时具体类型有其他方法也只有接口类型暴露出来的方法会被调用,也就是说一个具体的类型可以实现很多的接口,在调用的时候看持有这个变量的类型时哪个接口,就可以调用该接口的方法,而其他接口在该类型中实现的方法是被隐藏的。

  • 空接口interface{}可以承接任意的值

  • 每一个具体类型的组基于他们相同的行为可以表示成一个接口类型,不需要修改具体类型的定义,这个地方感觉还是组合的思想

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // 出售数字文化产品如音乐,书籍,电影等
    type Artifact interface {
    Title() string
    Creator() []string
    Created() time.Time
    }
    type Text interface {
    Pages() int
    Words() int
    PageSize() int
    }
    type Audio interface {
    Stream() (io.Readerclose, error)
    RunningTime() time.Duration
    Format() string
    }
    type Video interface {
    Stream() (io.ReadCloser, error)
    RunningTime() time.Duration
    Format() string
    Resolution() (x, y int)
    }

flag.Value接口

flag.Duration方法

  • flag可以为程序预设命令行参数,会有一个参数缺省默认值
  • flag.Duration函数创建一个time.Duration类型的标记变量并且允许用户通过多种用户友好的方式来设置这个值的大小
1
2
3
4
5
6
7
8
var period = flag.Duration("period", 1 * time.Second, "Sleep duration")
func main() {
flag.Parse()
fmt.Printf("Sleeping for %v...", *period)
time.Sleep(*period)
fmt.Println("end")
}
  • 首先看flag.Duration方法,返回的是一个地址指着
1
2
3
func Duration(name string, value time.Duration, usage string) *time.Duration {
return CommandLine.Duration(name, value, usage)
}
  • 然后看CommandLine的结构体和其Duration方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
f := &FlagSet{
name: name,
errorHandling: errorHandling,
}
f.Usage = f.defaultUsage
return f
}
func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration {
p := new(time.Duration)
f.DurationVar(p, name, value, usage)
return p
}
func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
f.Var(newDurationValue(value, p), name, usage)
}
// 真正实现值注入的地方
func (f *FlagSet) Var(value Value, name string, usage string) {
// Remember the default value as a string; it won't change.
flag := &Flag{name, usage, value, value.String()}
_, alreadythere := f.formal[name]
if alreadythere {
var msg string
if f.name == "" {
msg = fmt.Sprintf("flag redefined: %s", name)
} else {
msg = fmt.Sprintf("%s flag redefined: %s", f.name, name)
}
fmt.Fprintln(f.Output(), msg)
panic(msg) // Happens only if flags are declared with identical names
}
if f.formal == nil {
f.formal = make(map[string]*Flag)
}
f.formal[name] = flag
}
  • 实际上所有的Flag值保存在FlagSet结构体中的formal(map[string] *Flag)中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
type FlagSet struct {
// Usage is the function called when an error occurs while parsing flags.
Usage func()
name string
parsed bool
actual map[string]*Flag
formal map[string]*Flag
args []string // arguments after flags
errorHandling ErrorHandling
output io.Writer // nil means stderr; use out() accessor
}
// A Flag represents the state of a flag.
type Flag struct {
Name string // name as it appears on command line
Usage string // help message
Value Value // value as set
DefValue string // default value (as text); for usage message
}
// Value is the interface to the dynamic value stored in a flag.
type Value interface {
String() string
Set(string) error
}

flag.Parse()方法

  • 首先看Parse()方法的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
func Parse() {
// Ignore errors; CommandLine is set for ExitOnError.
CommandLine.Parse(os.Args[1:])
}
// flags初始化默认值之后,flag值使用之前,实际上是从命令行读取参数值对默认值进行覆盖操作
func (f *FlagSet) Parse(arguments []string) error {
f.parsed = true
f.args = arguments
for {
seen, err := f.parseOne()
if seen {
continue
}
if err == nil {
break
}
switch f.errorHandling {
case ContinueOnError:
return err
case ExitOnError:
os.Exit(2)
case PanicOnError:
panic(err)
}
}
return nil
}
//
func (f *FlagSet) parseOne() (bool, error) {
if len(f.args) == 0 {
return false, nil
}
s := f.args[0]
if len(s) < 2 || s[0] != '-' {
return false, nil
}
numMinuses := 1
if s[1] == '-' {
numMinuses++
if len(s) == 2 { // "--" terminates the flags
f.args = f.args[1:]
return false, nil
}
}
name := s[numMinuses:]
if len(name) == 0 || name[0] == '-' || name[0] == '=' {
return false, f.failf("bad flag syntax: %s", s)
}
// it's a flag. does it have an argument?
f.args = f.args[1:]
hasValue := false
value := ""
for i := 1; i < len(name); i++ { // equals cannot be first
if name[i] == '=' {
value = name[i+1:]
hasValue = true
name = name[0:i]
break
}
}
m := f.formal
flag, alreadythere := m[name] // BUG
if !alreadythere {
if name == "help" || name == "h" { // special case for nice help message.
f.usage()
return false, ErrHelp
}
return false, f.failf("flag provided but not defined: -%s", name)
}
if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg
if hasValue {
if err := fv.Set(value); err != nil {
return false, f.failf("invalid boolean value %q for -%s: %v", value, name, err)
}
} else {
if err := fv.Set("true"); err != nil {
return false, f.failf("invalid boolean flag %s: %v", name, err)
}
}
} else {
// It must have a value, which might be the next argument.
// 在这里进行解析替换
if !hasValue && len(f.args) > 0 {
// value is the next arg
hasValue = true
value, f.args = f.args[0], f.args[1:]
}
if !hasValue {
return false, f.failf("flag needs an argument: -%s", name)
}
// set设置
if err := flag.Value.Set(value); err != nil {
return false, f.failf("invalid value %q for flag -%s: %v", value, name, err)
}
}
if f.actual == nil {
f.actual = make(map[string]*Flag)
}
f.actual[name] = flag
return true, nil
}
// Duration的Set方法
func (d *durationValue) Set(s string) error {
v, err := time.ParseDuration(s)
if err != nil {
err = errParse
}
*d = durationValue(v)
return err
}
func ParseDuration(s string) (Duration, error) {
// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
orig := s
var d int64
neg := false
// Consume [-+]?
if s != "" {
c := s[0]
if c == '-' || c == '+' {
neg = c == '-'
s = s[1:]
}
}
// Special case: if all that is left is "0", this is zero.
if s == "0" {
return 0, nil
}
if s == "" {
return 0, errors.New("time: invalid duration " + orig)
}
for s != "" {
var (
v, f int64 // integers before, after decimal point
scale float64 = 1 // value = v + f/scale
)
var err error
// The next character must be [0-9.]
if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') {
return 0, errors.New("time: invalid duration " + orig)
}
// Consume [0-9]*
pl := len(s)
v, s, err = leadingInt(s)
if err != nil {
return 0, errors.New("time: invalid duration " + orig)
}
pre := pl != len(s) // whether we consumed anything before a period
// Consume (\.[0-9]*)?
post := false
if s != "" && s[0] == '.' {
s = s[1:]
pl := len(s)
f, scale, s = leadingFraction(s)
post = pl != len(s)
}
if !pre && !post {
// no digits (e.g. ".s" or "-.s")
return 0, errors.New("time: invalid duration " + orig)
}
// Consume unit.
i := 0
for ; i < len(s); i++ {
c := s[i]
if c == '.' || '0' <= c && c <= '9' {
break
}
}
if i == 0 {
return 0, errors.New("time: missing unit in duration " + orig)
}
u := s[:i]
s = s[i:]
unit, ok := unitMap[u]
if !ok {
return 0, errors.New("time: unknown unit " + u + " in duration " + orig)
}
if v > (1<<63-1)/unit {
// overflow
return 0, errors.New("time: invalid duration " + orig)
}
v *= unit
if f > 0 {
// float64 is needed to be nanosecond accurate for fractions of hours.
// v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit)
v += int64(float64(f) * (float64(unit) / scale))
if v < 0 {
// overflow
return 0, errors.New("time: invalid duration " + orig)
}
}
d += v
if d < 0 {
// overflow
return 0, errors.New("time: invalid duration " + orig)
}
}
if neg {
d = -d
}
return Duration(d), nil
}

接口值

  • 接口值指一个具体的类型和那个类型的值,也称为接口的动态类型和动态值
  • go中提供类型信息的值实际上是类型描述符,类型时编译期的概念
  • 接口的零值指的是期类型和值的部分都是nil
  • 不包含任何值的nil接口和一个刚好包含nil指针的接口值是不同的

go中排序算法的实现

sort接口

1
2
3
4
5
6
7
package sort
// 排序接口只规定了三个方法,序列长度,两个元素的比较结果,交换两个元素的方式
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}

go中排序函数的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 实现了前面Interface的结构体都可以进行排序,go中排序使用的是改进后的快排
func Sort(data Interface) {
n := data.Len()
quickSort(data, 0, n, maxDepth(n))
}
// 堆排序最大深度
func maxDepth(n int) int {
var depth int
for i := n; i > 0; i >>= 1 {
depth++
}
return depth * 2
}
func quickSort(data Interface, a, b, maxDepth int) {
for b-a > 12 { // Use ShellSort for slices <= 12 elements
if maxDepth == 0 {
// 进行堆排序
heapSort(data, a, b)
return
}
maxDepth--
// 获取pivot,进行快排
mlo, mhi := doPivot(data, a, b)
// Avoiding recursion on the larger subproblem guarantees
// a stack depth of at most lg(b-a).
if mlo-a < b-mhi {
quickSort(data, a, mlo, maxDepth)
a = mhi // i.e., quickSort(data, mhi, b)
} else {
quickSort(data, mhi, b, maxDepth)
b = mlo // i.e., quickSort(data, a, mlo)
}
}
if b-a > 1 {
// 希尔排序
// It could be written in this simplified form cause b-a <= 12
for i := a + 6; i < b; i++ {
if data.Less(i, i-6) {
data.Swap(i, i-6)
}
}
insertionSort(data, a, b)
}
}

总结

  • interface是一种类型
  • interfacde{}是空接口,和Java中Object类似,所有的结构体都实现了空接口
  • 如果一个类型实现了一个interface的所有方法,则该类型实现了该接口
  • interface变量存储的是实现结构体的值,并且只会暴露接口公开的值和方法
  • 判断interface存储的类型可以使用断言来实现。value, ok := interfaceObject.(assertType)
  • interface的接收者是类型值还是指针在定义的时候没有严格规定,但是go语言实际上是只支持值传递。golang的语法糖会将函数里调用的指针进行隐式的转换成值,但是反过来是不行的。函数调用优先使用指针就OK了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    type I interface {
    Get() int
    Set(int)
    }
    type SS struct {
    Age int
    }
    func (s SS) Get() int {
    return s.Age
    }
    func (s SS) Set(age int) {
    s.Age = age
    }
    func f(i I) {
    i.Set(10)
    fmt.Println(i.Get())
    }
    func main() {
    ss := SS{}
    f(&ss) //指针参数,无论函数的接收者是值还是指针都不会保存
    f(ss) //值参数,函数的接收者是值不会报错,是指针会编译报错
    }
Donate comment here