Fortunately Go contributes a very powerful version of interfaces to the abstraction conversation. Go interfaces describe groups of behaviors in the form of method signatures that implementors of the interface must implement. So far this doesn’t sound unlike Java interfaces. Let’s take a look at two Go interfaces in Positioner and Distancer interfaces.

Positioner and Distancer interfaces type Positioner interface { Coordinates() Point } type Distancer interface { DistanceTo(p Positioner) float64 }

Notice that interface s are themselves types. This is important, because it allows us to define function and method signatures that accept interfaces as arguments. Positioner 's Coordinates() method should provide us the position of any implementor in terms of a Point . Distancer 's DistanceTo() method will calculate the distance between any implementor and a Positioner .

Note It is idiomatic in Go for interface names to be suffixed with “-er.”

However, unlike Java interfaces, where we might write:

public class Point implements Positioner, Distancer { // implementation omitted... }

Go does not have an implements keyword. In fact, if you think about the way we’ve defined structs and methods so far, the only place in which we indicated any attachment between a struct and a method is in the method signature itself, in the form of the receiver parameter. So how does this work in Go?

Let’s talk about how interfaces are satsified.

In languages like Java, an interfaces is satisfied explicitly. Classes are tagged with implements Interface . The compiler then looks up that interface and identifies all of the method signatures defined by it. It then examines the class to ensure that all of those method signatures have a concrete implementation in either the class itself or one of its parents.

In Go, interfaces are satisfied implicitly. We do not tag structs in any way. For a type to implement an interface, it simply needs to implement all of the method signatures defined by that interface. When we use a type in the context of an interface (e.g. we pass a type into a function that expects an interface as one of its arguments), the compiler will check to see if that type satisfies the interface.

Let’s see how this falls out in practice.

First, let’s define a function that can calculate the distance between two Positioner s (Calculates the distance between two Positioner s).

Calculates the distance between two Positioner s func distanceBetween(a Positioner, b Positioner) float64 { p := a.Coordinates() q := b.Coordinates() sqOfXDist := math.Pow(p.X-q.X, 2) sqOfYDist := math.Pow(p.Y-q.Y, 2) return math.Sqrt(sqOfXDist + sqOfYDist) }

Next, in Point satisfies Positioner and Distancer , we satisfy both interfaces for Point .

Point satisfies Positioner and Distancer func (p Point) Coordinates() Point { return p } func (p Point) DistanceTo(pos Positioner) float64 { return distanceBetween(p, pos) }

In ColorPoint satisfies Positioner and Distancer , we satisfy both interfaces for ColorPoint .

ColorPoint satisfies Positioner and Distancer func (cp ColorPoint) Coordinates() Point { return cp.Point } func (cp ColorPoint) DistanceTo(pos Positioner) float64 { return distanceBetween(cp, pos) }

This all results in our ability to interchange Point s and ColorPoint s in calls to DistanceTo() (Calculating the distance between Point and ColorPoint ). The output of these calls is found in Output of calculating the distance between Point and ColorPoint .

Calculating the distance between Point and ColorPoint fmt.Printf("Dist b/w p and q = %v

", p.DistanceTo(r)) fmt.Printf("Dist b/w q and p = %v

", r.DistanceTo(p))

Output of calculating the distance between Point and ColorPoint $ go run point.go Dist b/w p and q = 2 Dist b/w q and p = 2

At first glance this may not seem so impressive, nor may it seem to be that great of an advantage over what’s available to us in Java. The power, however, is hiding just under the surface. When we think about implementing an interface in Java, we usually are thinking in terms of taxonomy. Classes extend other classes, and as we’ve previously stated, that means that a child class ought to be substitutable for its parent [LSP]. When we implement an interface in Java, we are also usually thinking that the class is a version of that interface. We might have expressed our Cartesian coordinate taxonomy in Java as:

public interface Coordinate { double distanceTo(Coordinate c); } public class Point implements Coordinate { // implementation omitted } public class ColorPoint extends Point implements Coordinate { // implementation omitted }

When we do this, we soon hear ourselves talking about Coordinate s as things, not as groups of behaviors. Let’s contrast this with Go’s implicit satisfaction by adding an additional example. Perhaps our program’s purpose is to keep track of animals in a wildlife preserve. It’s quite natural that we’d have an Animal type, and that type would have some way of keeping track of the animal’s current location ( Animal struct).

Animal struct type Animal struct { Name string X, Y float64 }

In order to perform our desired distance calculations, we need Animal to satisfy our two interfaces ( Animal satisfies Positioner and Distancer ).

Animal satisfies Positioner and Distancer func (a Animal) Coordinates() point.Point { return point.Point{X: a.X, Y: a.Y} } func (a Animal) DistanceTo(pos point.Positioner) float64 { thing := pos.Coordinates() sqOfXDist := math.Pow(a.X-thing.X, 2) sqOfYDist := math.Pow(a.Y-thing.Y, 2) return math.Sqrt(sqOfXDist + sqOfYDist) }

Note Because distanceBetween() was not exported from the point package, we cannot use it in the animal package. Sometimes you’ll run into this situation in Go, which prefers “dependency hygiene” over reuse.

And now, we can perform our desired calculations (Mixing Animal s and Point s using interfaces). We now know the distance between our penguin and our original point p , and we also know that given the proximity of the seal (Output of mixing Animal s and Point s using interfaces), our penguin needs to start running!

Mixing Animal s and Point s using interfaces penguin := animal.Animal{Name: "penguin", X: 1, Y: 1} seal := animal.Animal{Name: "seal", X: 1, Y: 4} fmt.Printf("Dist b/w penguin and seal = %v

", penguin.DistanceTo(seal)) fmt.Printf("Dist b/w penguin and point = %v

", penguin.DistanceTo(p))

Output of mixing Animal s and Point s using interfaces $ go run point.go Dist b/w penguin and seal = 3 Dist b/w penguin and point = 1

Now for the test. Is it proper to think of an Animal as being a Distancer or Positioner in terms of taxonomy? Not really. In fact, that seems like a coupling of concerns. And if we were implementing this program in Java, a naive translation would probably cause us to do the following:

public class Animal implements Positioner, Distancer

So to summarize, Go interfaces allow us to use arbitrary types in contexts expecting a particular interface type, as long as the type in question implements all of the methods defined by that interface. Since interfaces are satisfied implicitly, we’re no longer pressured toward treating interfaces as part of a type taxonomy. Instead, we’re able to focus on them as groups of related behaviors.