Go Interfaces
Go - Interfaces
Section titled “Go - Interfaces”In Go, an interface is a type that defines a set of method signatures. It specifies what methods a type must implement to “satisfy” or “implement” the interface. Unlike some other languages, Go’s interfaces are satisfied implicitly. If a type implements all the methods declared in an interface, it automatically satisfies that interface without needing an explicit implements keyword.
This implicit satisfaction makes Go’s interfaces flexible and powerful, promoting loose coupling and enabling polymorphism.
Defining an Interface
Section titled “Defining an Interface”/* define an interface */type InterfaceName interface { MethodName1(param1 type1, param2 type2) returnType1 MethodName2() (returnTypeA, returnTypeB) // ... other method signatures}
/* define a struct (or any other named type) */type ConcreteType struct { // fields}
/* implement interface methods for ConcreteType */// Note: The receiver can be a value (ConcreteType) or a pointer (*ConcreteType)func (c ConcreteType) MethodName1(param1 type1, param2 type2) returnType1 { // method implementation return // ... appropriate value}
func (c ConcreteType) MethodName2() (returnTypeA, returnTypeB) { // method implementation return // ... appropriate values}Example: Geometric Shapes
Section titled “Example: Geometric Shapes”Let’s define an interface Shape with a method Area(). Then we’ll create Circle and Rectangle types that implement this interface.
package main
import ( "fmt" "math")
// Shape interface defines a contract for types that can calculate their area.type Shape interface { Area() float64}
// Circle type with a radius.type Circle struct { Radius float64}
// Rectangle type with width and height.type Rectangle struct { Width, Height float64}
// Circle implements the Shape interface.func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius}
// Rectangle implements the Shape interface.func (r Rectangle) Area() float64 { return r.Width * r.Height}
// printArea takes any Shape and prints its area.// This demonstrates polymorphism.func printArea(s Shape) { fmt.Printf("Area of shape: %f\n", s.Area())}
// calculateTotalArea calculates the sum of areas for a slice of Shapes.func calculateTotalArea(shapes []Shape) float64 { totalArea := 0.0 for _, s := range shapes { totalArea += s.Area() } return totalArea}
func main() { circle := Circle{Radius: 5} rectangle := Rectangle{Width: 10, Height: 5}
fmt.Printf("Circle area: %f\n", circle.Area()) // Direct call fmt.Printf("Rectangle area: %f\n", rectangle.Area()) // Direct call
fmt.Println("\nUsing printArea function (polymorphism):") printArea(circle) // Circle satisfies Shape printArea(rectangle) // Rectangle satisfies Shape
shapes := []Shape{ // A slice of Shapes Circle{Radius: 2.5}, Rectangle{Width: 3, Height: 7}, Circle{Radius: 10}, } total := calculateTotalArea(shapes) fmt.Printf("\nTotal area of all shapes: %f\n", total)}When the above code is compiled and executed, it produces the following result:
Circle area: 78.539816Rectangle area: 50.000000
Using printArea function (polymorphism):Area of shape: 78.539816Area of shape: 50.000000
Total area of all shapes: 334.207943The Empty Interface: interface{}
Section titled “The Empty Interface: interface{}”An interface with no methods is called the empty interface, written as interface{}. Since the empty interface has no methods, any type implements it. This makes it useful for functions that need to handle values of unknown type. However, to use the underlying concrete value, you’ll need a type assertion or a type switch.
func describe(i interface{}) { fmt.Printf("Value: %v, Type: %T\n", i, i)}
// In main():// describe(42)// describe("hello")// describe(true)Type Assertions and Type Switches
Section titled “Type Assertions and Type Switches”When working with interface values, you often need to access the underlying concrete type or its specific methods. This is done using type assertions or type switches.
Type Assertion: value, ok := i.(ConcreteType)
Checks if the interface value i holds a concrete type ConcreteType and assigns it to value. ok is true if the assertion succeeds.
Type Switch: switch v := i.(type) { case ConcreteType1: ... case ConcreteType2: ... }
Permits several type assertions in series.
Interfaces are a cornerstone of Go’s design, enabling flexible and extensible systems. They are widely used in the standard library and are key to writing idiomatic Go code.