Golang interfaces
Similar to other languages, interfaces in go define a contract of method-signatures that implementors must have.
Unlike other languages, golang interfaces are implicit -- an object with all of the required methods automatically satisfies an interface.
In go, it is encouraged to keep very small interfaces (ex. 1 method). The larger the interface, the weaker the abstraction.
Similar to other languages, a param can be typed to accept an interface, which abstracts the actual type that is received.
NOTE:
go interfaces define a contract of methods, but not struct fields.
you'll need to define getters/setters for fields
Basics
Libraries do not need to expose interfaces in go, you can create them for the subset of methods that are useful to you.
- An interface is a promise that an object implements a set of method-signatures.
- objects are automatically a part of an interface if they have methods, there is no
implements Foo
- Empty interfaces match all oject types in go. (ex
fmt.Println()
can accept any type)// rectangle.go type Rectangle struct { width int height int } func (rect Rectangle) Area() int { return rect.width * rect.height }// area_calculator.go type AreaCalculator interface { Area() int }// main.go func LargestArea(a, b AreaCalculator) AreaCalculator { if a.Area() > b.Area() { return a } return b } func main() { rect1 := Rectangle{5, 5} rect2 := Rectangle{2, 10} fmt.Println(LargestArea(rect1, rect2) == rect1) }
Interfaces with Pointer Methods
When a method's
self
is a pointer, you must pass in a reference (&value
) to the object in the method call.
Reusing the last example// rectangle.go type Rectangle struct { width int height int } func (rect *Rectangle) Area() int { // <-- SEEME 'rect *Rectangle' indicates this method is added to a reference to this object return rect.width * rect.height }// area_calculator.go -- IDENTICAL type AreaCalculator interface { Area() int }// main.go func LargestArea(a, b AreaCalculator) AreaCalculator { if a.Area() > b.Area() { return a } return b } func main() { rect1 := Rectangle{5, 5} rect2 := Rectangle{2, 10} fmt.Println(rect1.Area()) fmt.Println(rect2.Area()) fmt.Println(LargestArea(&rect1, &rect2)) // <--- SEEME passing in reference!! }
Embedding in Interfaces
Embedding an interface within an interfaces includes it's method-signatures in the parent.
type Walker interface { Walk nil } type BubblegumChewer interface { ChewGum nil } // includes all methods from previous two interfaces type BubblegumChewingWalker interface { Walker BubblegumChewer }
Embedding in Structs (inherit methods)
By embedding interfaces in a struct, you inherit the methods from the wrapped object.
package main import "fmt" // interface type Greeter interface { Greet() string } // English/French implementations of Greeter type EnglishGreeter struct{} func (this *EnglishGreeter) Greet() string { return "hello" } type FrenchGreeter struct{} func (this *FrenchGreeter) Greet() string { return "bonjours" } // Inherits Greet(), but can define it's own fields/methods type GreeterWrapper struct { Greeter } func (this *GreeterWrapper) GreetByName(name string) string { return fmt.Sprintf("%s, %s", this.Greet(), name) } func main() { english := EnglishGreeter{} englishWrapper := GreeterWrapper{Greeter: &english} fmt.Println(englishWrapper.Greet()) fmt.Println(englishWrapper.GreetByName("will")) french := FrenchGreeter{} frenchWrapper := GreeterWrapper{Greeter: &french} fmt.Println(frenchWrapper.Greet()) fmt.Println(frenchWrapper.GreetByName("will")) } // hello // hello, will // bonjours // bonjours, will
Empty Interfaces
Empty interfaces will accept any type within go.
It is used for example byfmt.Println()
so that it can accept any type of parameter.func typeInfo(obj interface{}) string { fmt.Printf("(%v, %T)\n", obj, obj) }
Cast as Concretion
See golang variables for more details
var foo MyInterface foo = MyConcretion{} castFoo := foo.(MyConcretion)