02.6.md 12.3 KB
Newer Older
A
astaxie 已提交
1 2 3 4 5 6 7 8
# 2.6 Interface

## Interface

One of the subtlest design features in Go are interfaces. After reading this section, you will likely be impressed by their implementation.

### What is an interface

A
Anchor 已提交
9
In short, an interface is a set of methods that we use to define a set of actions.
A
astaxie 已提交
10 11 12

Like the examples in previous sections, both Student and Employee can `SayHi()`, but they don't do the same thing.

A
Anchor 已提交
13
Let's do some more work. We'll add one more method `Sing()` to them, along with the `BorrowMoney()` method to Student and the `SpendSalary()` method to Employee.
A
astaxie 已提交
14

A
Anchor 已提交
15
Now, Student has three methods called `SayHi()`, `Sing()` and `BorrowMoney()`, and Employee has `SayHi()`, `Sing()` and `SpendSalary()`.
A
astaxie 已提交
16

A
Anchor 已提交
17
This combination of methods is called an interface and is implemented by both Student and Employee. So, Student and Employee implement the interface: `SayHi()` and `Sing()`. At the same time, Employee doesn't implement the interface: `SayHi()`, `Sing()`, `BorrowMoney()`, and Student doesn't implement the interface: `SayHi()`, `Sing()`, `SpendSalary()`. This is because Employee doesn't have the method `BorrowMoney()` and Student doesn't have the method `SpendSalary()`.
A
astaxie 已提交
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

### Type of Interface

An interface defines a set of methods, so if a type implements all the methods we say that it implements the interface.

	type Human struct {
		name  string
		age   int
		phone string
	}

	type Student struct {
		Human
		school string
		loan   float32
	}

	type Employee struct {
		Human
		company string
		money   float32
	}

	func (h *Human) SayHi() {
		fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
	}

	func (h *Human) Sing(lyrics string) {
		fmt.Println("La la, la la la, la la la la la...", lyrics)
	}

	func (h *Human) Guzzle(beerStein string) {
		fmt.Println("Guzzle Guzzle Guzzle...", beerStein)
	}

	// Employee overloads Sayhi
	func (e *Employee) SayHi() {
		fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
			e.company, e.phone) //Yes you can split into 2 lines here.
	}

	func (s *Student) BorrowMoney(amount float32) {
		s.loan += amount // (again and again and...)
	}

	func (e *Employee) SpendSalary(amount float32) {
		e.money -= amount // More vodka please!!! Get me through the day!
	}

	// define interface
	type Men interface {
		SayHi()
		Sing(lyrics string)
		Guzzle(beerStein string)
	}

	type YoungChap interface {
		SayHi()
		Sing(song string)
		BorrowMoney(amount float32)
	}

	type ElderlyGent interface {
		SayHi()
		Sing(song string)
		SpendSalary(amount float32)
	}

A
Anchor 已提交
86
We know that an interface can be implemented by any type, and one type can implement many interfaces simultaneously.
A
astaxie 已提交
87

A
Anchor 已提交
88
Note that any type implements the empty interface `interface{}` because it doesn't have any methods and all types have zero methods by default.
A
astaxie 已提交
89 90 91 92 93

### Value of interface

So what kind of values can be put in the interface? If we define a variable as a type interface, any type that implements the interface can assigned to this variable.

A
Anchor 已提交
94
Like the above example, if we define a variable "m" as interface Men, then any one of Student, Human or Employee can be assigned to "m". So we could have a slice of Men, and any type that implements interface Men can assign to this slice. Be aware however that the slice of interface doesn't have the same behavior as a slice of other types.
A
astaxie 已提交
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

	package main
	
	import "fmt"
	
	type Human struct {
		name  string
		age   int
		phone string
	}
	
	type Student struct {
		Human
		school string
		loan   float32
	}
	
	type Employee struct {
		Human
		company string
		money   float32
	}
	
	func (h Human) SayHi() {
		fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
	}
	
	func (h Human) Sing(lyrics string) {
		fmt.Println("La la la la...", lyrics)
	}
	
	func (e Employee) SayHi() {
		fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
			e.company, e.phone) //Yes you can split into 2 lines here.
	}
	
	// Interface Men implemented by Human, Student and Employee
	type Men interface {
		SayHi()
		Sing(lyrics string)
	}
	
	func main() {
		mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
		paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
		sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
		Tom := Employee{Human{"Sam", 36, "444-222-XXX"}, "Things Ltd.", 5000}
	
		// define interface i
		var i Men
	
		//i can store Student
		i = mike
		fmt.Println("This is Mike, a Student:")
		i.SayHi()
		i.Sing("November rain")
	
		//i can store Employee
		i = Tom
		fmt.Println("This is Tom, an Employee:")
		i.SayHi()
		i.Sing("Born to be wild")
	
		// slice of Men
		fmt.Println("Let's use a slice of Men and see what happens")
		x := make([]Men, 3)
		// these three elements are different types but they all implemented interface Men
		x[0], x[1], x[2] = paul, sam, mike
	
		for _, value := range x {
			value.SayHi()
		}
	}

	
An interface is a set of abstract methods, and can be implemented by non-interface types. It cannot therefore implement itself.

### Empty interface

A
Anchor 已提交
174
An empty interface is an interface that doesn't contain any methods, so all types implement an empty interface. This fact is very useful when we want to store all types at some point, and is similar to void* in C.
A
astaxie 已提交
175 176 177 178 179 180 181 182 183 184 185 186 187

	// define a as empty interface
	var a interface{}
	var i int = 5
	s := "Hello world"
	// a can store value of any type
	a = i
	a = s

If a function uses an empty interface as its argument type, it can accept any type; if a function uses empty as its return value type, it can return any type.

### Method arguments of an interface

A
Anchor 已提交
188
Any variable can be used in an interface. So how can we use this feature to pass any type of variable to a function?
A
astaxie 已提交
189

A
Anchor 已提交
190
For example we use fmt.Println a lot, but have you ever noticed that it can accept any type of argument? Looking at the open source code of fmt, we see the following definition.
A
astaxie 已提交
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

	type Stringer interface {
    		String() string
	}
	
This means any type that implements interface Stringer can be passed to fmt.Println as an argument. Let's prove it.

	package main

	import (
		"fmt"
		"strconv"
	)

	type Human struct {
		name  string
		age   int
		phone string
	}

	// Human implemented fmt.Stringer
	func (h Human) String() string {
		return "Name:" + h.name + ", Age:" + strconv.Itoa(h.age) + " years, Contact:" + h.phone
	}

	func main() {
		Bob := Human{"Bob", 39, "000-7777-XXX"}
		fmt.Println("This Human is : ", Bob)
	}

	
Looking back to the example of Box, you will find that Color implements interface Stringer as well, so we are able to customize the print format. If we don't implement this interface, fmt.Println prints the type with its default format.

	fmt.Println("The biggest one is", boxes.BiggestsColor().String())
	fmt.Println("The biggest one is", boxes.BiggestsColor())
	
Attention: If the type implemented the interface `error`, fmt will call `error()`, so you don't have to implement Stringer at this point.

### Type of variable in an interface

A
Anchor 已提交
231
If a variable is the type that implements an interface, we know that any other type that implements the same interface can be assigned to this variable. The question is how can we know the specific type stored in the interface. There are two ways which I will show you. 
A
astaxie 已提交
232 233 234

- Assertion of Comma-ok pattern

A
Anchor 已提交
235
Go has the syntax `value, ok := element.(T)`. This checks to see if the variable is the type that we expect, where "value" is the value of the variable, "ok" is a variable of boolean type, "element" is the interface variable and the T is the type of assertion.
A
astaxie 已提交
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324

If the element is the type that we expect, ok will be true, false otherwise.

Let's use an example to see more clearly.

	package main
	
	import (
		"fmt"
		"strconv"
	)
	
	type Element interface{}
	type List []Element
	
	type Person struct {
		name string
		age  int
	}
	
	func (p Person) String() string {
		return "(name: " + p.name + " - age: 	" + strconv.Itoa(p.age) + " years)"
	}
	
	func main() {
		list := make(List, 3)
		list[0] = 1       // an int
		list[1] = "Hello" // a string
		list[2] = Person{"Dennis", 70}
	
		for index, element := range list {
			if value, ok := element.(int); ok {
				fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
			} else if value, ok := element.(string); ok {
				fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
			} else if value, ok := element.(Person); ok {
				fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
			} else {
				fmt.Println("list[%d] is of a different type", index)
			}
		}
	}

It's quite easy to use this pattern, but if we have many types to test, we'd better use `switch`.

- switch test

Let's use `switch` to rewrite the above example.
	
	package main
	
	import (
		"fmt"
		"strconv"
	)
	
	type Element interface{}
	type List []Element
	
	type Person struct {
		name string
		age  int
	}
	
	func (p Person) String() string {
		return "(name: " + p.name + " - age: " + strconv.Itoa(p.age) + " years)"
	}
	
	func main() {
		list := make(List, 3)
		list[0] = 1       //an int
		list[1] = "Hello" //a string
		list[2] = Person{"Dennis", 70}
	
		for index, element := range list {
			switch value := element.(type) {
			case int:
				fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
			case string:
				fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
			case Person:
				fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
			default:
				fmt.Println("list[%d] is of a different type", index)
			}
		}
	}

	
A
Anchor 已提交
325
One thing you should remember is that `element.(type)` cannot be used outside of the `switch` body, which means in that case you have to use the `comma-ok` pattern .
A
astaxie 已提交
326 327 328

### Embedded interfaces

A
Anchor 已提交
329
The most beautiful thing is that Go has a lot of built-in logic syntax, such as anonymous fields in struct. Not suprisingly, we can use interfaces as anonymous fields as well, but we call them `Embedded interfaces`. Here, we follow the same rules as anonymous fields. More specifically, if an interface has another interface embedded within it, it will have as if it has all the methods that the embedded interface has.
A
astaxie 已提交
330

A
Anchor 已提交
331
We can see that the source file in `container/heap` has the following definition:
A
astaxie 已提交
332 333 334 335 336 337 338

	type Interface interface {
	    	sort.Interface // embedded sort.Interface
	    	Push(x interface{}) //a Push method to push elements into the heap
	    	Pop() interface{} //a Pop elements that pops elements from the heap
	}

A
Anchor 已提交
339
We see that `sort.Interface` is an embedded interface, so the above Interface has the three methods contained within the `sort.Interface` implicitly.
A
astaxie 已提交
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362

	type Interface interface {
	    	// Len is the number of elements in the collection.
	    	Len() int
	    	// Less returns whether the element with index i should sort
	    	// before the element with index j.
	    	Less(i, j int) bool
	    	// Swap swaps the elements with indexes i and j.
	    	Swap(i, j int)
	}
	
Another example is the `io.ReadWriter` in package `io`.

	// io.ReadWriter
	type ReadWriter interface {
    		Reader
    		Writer
	}
	
### Reflection

Reflection in Go is used for determining information at runtime. We use the `reflect` package, and this official [article](http://golang.org/doc/articles/laws_of_reflection.html) explains how reflect works in Go.

A
Anchor 已提交
363
There are three steps involved when using reflect. First, we need to convert an interface to reflect types (reflect.Type or reflect.Value, this depends on the situation).
A
astaxie 已提交
364 365 366 367

	t := reflect.TypeOf(i)    // get meta-data in type i, and use t to get all elements
	v := reflect.ValueOf(i)   // get actual value in type i, and use v to change its value
	
368
After that, we can convert the reflected types to get the values that we need.
A
astaxie 已提交
369 370 371 372 373 374 375

	var x float64 = 3.4
	v := reflect.ValueOf(x)
	fmt.Println("type:", v.Type())
	fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
	fmt.Println("value:", v.Float())
	
376
Finally, if we want to change the values of the reflected types, we need to make it modifiable. As discussed earlier, there is a difference between pass by value and pass by reference. The following code will not compile.
A
astaxie 已提交
377 378 379 380 381

	var x float64 = 3.4
	v := reflect.ValueOf(x)
	v.SetFloat(7.1)
	
A
Anchor 已提交
382
Instead, we must use the following code to change the values from reflect types.
A
astaxie 已提交
383 384 385 386 387

	var x float64 = 3.4
	p := reflect.ValueOf(&x)
	v := p.Elem()
	v.SetFloat(7.1)
A
Anchor 已提交
388 389

We have just discussed the basics of reflection, however you must practice more in order to understand more.
A
astaxie 已提交
390 391 392 393 394 395

## Links

- [Directory](preface.md)
- Previous section: [Object-oriented](02.5.md)
- Next section: [Concurrency](02.7.md)