13. interface
|
Be Aware: Learning to use interfaces in Go is extremely difficult - the basics are easy but it will take some practice before you know how to design your own interfaces. |
An interface is a set of methods. To turn it around, the methods implemented by a concrete type such as a struct form the interface of that type.
Let’s focus on the method set aspect of interfaces first.
In Go, suppose you have an interface for a Duck:
1 type Duck interface {
2 Quack()
3 }
Any struct that has a Quack method will implement the Duck interface implicitly and can be used as a Duck.
1 package main
2
3 import "fmt"
4
5 type Duck interface {
6 Quack()
7 }
8
9 type Donald struct {
10 }
11
12 func (d Donald) Quack() {
13 fmt.Println("quack quack!")
14 }
15
16 type Daisy struct {
17 }
18
19 func (d Daisy) Quack() {
20 fmt.Println("~quack ~quack")
21 }
22
23 func main() {
24 var duck Duck
25 duck = Donald{}
26 duck.Quack()
27
28 duck = Daisy{}
29 duck.Quack()
30 }
with output:
quack quack!
~quack ~quack
If you have another struct, Dog, which doesn’t have a Quack method and you tried to use it as a Duck then you’ll get a compile time error:
1 package main
2
3 import "fmt"
4
5 type Duck interface {
6 Quack()
7 }
8
9 type Donald struct {
10 }
11
12 func (d Donald) Quack() {
13 fmt.Println("quack quack!")
14 }
15
16 type Daisy struct {
17 }
18
19 func (d Daisy) Quack() {
20 fmt.Println("~quack ~quack")
21 }
22
23 type Dog struct {
24 }
25
26 func (d Dog) Bark() {
27 fmt.Println("woof woof")
28 }
29
30 func main() {
31 var duck Duck
32 duck = Donald{}
33 duck.Quack()
34
35 duck = Daisy{}
36 duck.Quack()
37
38 duck = Dog{}
39 duck.Quack()
40 }
with output:
main.go:38: cannot use Dog literal (type Dog) as type Duck in assignment:
Dog does not implement Duck (missing Quack method)
Earlier we had seen an example of a Square struct and an Area function:
1 package main
2
3 import "fmt"
4
5 type Square struct {
6 Width, Height int
7 }
8
9 func (s Square) Area() int {
10 return s.Width * s.Height
11 }
12
13 func main() {
14 r1 := Square{5, 5}
15 fmt.Println("Square is: ", r1)
16 fmt.Println("Square area is: ", r1.Area())
17 }
with output:
Square is: {5 5}
Square area is: 25
Note: Although not used here, it is idiomatic in Go for interface names to be suffixed with “-er” (like Positioner, Distancer).
Let us now abstract out the Area function into an interface called Geometry. Geometry is an interface and has a single function Area that returns an int as follows:
1 type Geometry interface {
2 Area() int
3 }
Let us write the full program:
Program: geometry.go
1 package main
2
3 import "fmt"
4
5 type Square struct {
6 Width, Height int
7 }
8
9 type Geometry interface {
10 Area() int
11 }
12
13 // To implement an interface in Go, we just need to implement all the methods
14 // in the interface
15 func (s Square) Area() int {
16 return s.Width * s.Height
17 }
18
19 // If a variable has an interface type, then we can call methods that are in
20 // the named interface. Here is a generic measure function taking advantage of
21 // this to work on any Geometry.
22 func measure(g Geometry) {
23 fmt.Println(g)
24 fmt.Println(g.Area())
25 }
26
27 func main() {
28 s := Square{Width: 3, Height: 4}
29
30 // The Square struct type implements the Geometry interface so we can
31 // use instances of these structs as arguments to measure.
32 measure(s)
33 }
The output is:
{3 4}
12
Here’s an example1 of a simple Animal interface.
13.1 The interface{} type
It’s possible to define an interface without any methods. Such an interface is known as an empty interface, and it’s denoted by interface{}. Since there are no methods, all types will satisfy this interface.
That means if you write a function that takes an interface{} value as a parameter, you can supply that function with any value. So, this function:
1 func DoSomething(v interface{}) {
2 // ...
3 }
will accept any parameter whatsoever.
Here’s where it gets confusing: inside of the DoSomething function, what is v’s type? Beginners are led to believe that “v is of any type”, but that is wrong. v is not of any type; it is of interface{} type. Wait, what? When passing a value into the DoSomething function, the Go runtime will perform a type conversion (if necessary), and convert the value to an interface{} value. All values have exactly one type at runtime, and v’s one static type is interface{}.
This should leave you wondering: ok, so if a conversion is taking place, what is actually being passed into a function that takes an interface{} value?
If you understand that an interface value is two words wide and it contains a pointer to the underlying data, that’s typically enough to avoid common pitfalls.
This topic is adapted from blog posts this2 and this3
|
Exercise 15Declare an interface named Speaker with a method named SayHello. Declare a struct named English that represents a person who speaks english, declare a struct named Chinese for someone who speaks chinese and a struct named Indian for someone who speaks hindi. Implement the Speaker interface for each struct using a pointer receiver and these literal strings “I speak English!”, “I speak Chinese!” and “I speak Hindi!”. |