Golang Tutorial
Fundamentals
Control Statements
Functions & Methods
Structure
Arrays & Slices
String
Pointers
Interfaces
Concurrency
Polymorphism is one of the four fundamental Object-Oriented Programming (OOP) concepts, the others being inheritance, encapsulation, and abstraction. In Go, polymorphism is achieved through interfaces. Interfaces allow different types to be treated as if they were the same type when it comes to specific behaviors.
Let's dive into how polymorphism works using interfaces in Go.
An interface is a type that specifies a set of method signatures (behavior), but it doesn't implement the methods itself. A type (either struct or non-struct) is said to implement an interface if it provides definitions for all the methods declared by that interface.
Let's say we have different shapes and we want to calculate their area.
type Shape interface { Area() float64 }
type Rectangle struct { Width float64 Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height } type Circle struct { Radius float64 } func (c Circle) Area() float64 { return 3.14 * c.Radius * c.Radius }
Both Rectangle
and Circle
implement the Shape
interface because they both have an Area
method.
Now, we can create a function that can operate on any shape, leveraging the power of polymorphism.
func PrintArea(s Shape) { fmt.Printf("Area: %f\n", s.Area()) }
func main() { r := Rectangle{Width: 10, Height: 5} c := Circle{Radius: 10} PrintArea(r) // Area: 50.000000 PrintArea(c) // Area: 314.000000 }
In the main
function, you can see that we're able to pass both a Rectangle
and a Circle
to the PrintArea
function. This is polymorphism in action! The correct Area
method is called for each type at runtime.
In Go, interfaces are a powerful way to achieve polymorphism. They allow you to treat different types as if they were the same type based on their behaviors, making your code more flexible and extensible. Remember, unlike some OOP languages, Go doesn't support inheritance in the classical sense, so interfaces are the primary tool for achieving polymorphic behavior.
Polymorphism in Golang using Interfaces:
package main import "fmt" // Shape interface type Shape interface { Area() float64 } // Circle type type Circle struct { Radius float64 } // Rectangle type type Rectangle struct { Width, Height float64 } // Implement Area method for Circle func (c Circle) Area() float64 { return 3.14 * c.Radius * c.Radius } // Implement Area method for Rectangle func (r Rectangle) Area() float64 { return r.Width * r.Height } func printArea(s Shape) { fmt.Println("Area:", s.Area()) } func main() { circle := Circle{Radius: 5.0} rectangle := Rectangle{Width: 4.0, Height: 6.0} printArea(circle) // Output: Area: 78.5 printArea(rectangle) // Output: Area: 24 }
Golang Interface Polymorphism Examples:
package main import "fmt" // Printer interface type Printer interface { Print() } // Circle type type Circle struct{} // Rectangle type type Rectangle struct{} // Implement Print method for Circle func (c Circle) Print() { fmt.Println("Printing Circle") } // Implement Print method for Rectangle func (r Rectangle) Print() { fmt.Println("Printing Rectangle") } func main() { circle := Circle{} rectangle := Rectangle{} printShape(circle) // Output: Printing Circle printShape(rectangle) // Output: Printing Rectangle } func printShape(p Printer) { p.Print() }
Implementing Polymorphism with Interfaces in Golang:
package main import "fmt" // Animal interface type Animal interface { Speak() string } // Dog type type Dog struct{} // Cat type type Cat struct{} // Implement Speak method for Dog func (d Dog) Speak() string { return "Woof!" } // Implement Speak method for Cat func (c Cat) Speak() string { return "Meow!" } func main() { dog := Dog{} cat := Cat{} speakAndPrint(dog) // Output: Woof! speakAndPrint(cat) // Output: Meow! } func speakAndPrint(a Animal) { fmt.Println(a.Speak()) }
Dynamic Polymorphism with Interfaces in Golang:
package main import "fmt" // Shape interface type Shape interface { Area() float64 } // Circle type type Circle struct { Radius float64 } // Rectangle type type Rectangle struct { Width, Height float64 } // Implement Area method for Circle func (c Circle) Area() float64 { return 3.14 * c.Radius * c.Radius } // Implement Area method for Rectangle func (r Rectangle) Area() float64 { return r.Width * r.Height } func main() { shapes := []Shape{ Circle{Radius: 5.0}, Rectangle{Width: 4.0, Height: 6.0}, } for _, shape := range shapes { fmt.Println("Area:", shape.Area()) } }
Golang Interface Type Assertions for Polymorphism:
package main import "fmt" // Shape interface type Shape interface { Area() float64 } // Circle type type Circle struct { Radius float64 } // Rectangle type type Rectangle struct { Width, Height float64 } // Implement Area method for Circle func (c Circle) Area() float64 { return 3.14 * c.Radius * c.Radius } // Implement Area method for Rectangle func (r Rectangle) Area() float64 { return r.Width * r.Height } func main() { shapes := []Shape{ Circle{Radius: 5.0}, Rectangle{Width: 4.0, Height: 6.0}, } for _, shape := range shapes { if circle, ok := shape.(Circle); ok { fmt.Println("Circle Area:", circle.Area()) } else if rectangle, ok := shape.(Rectangle); ok { fmt.Println("Rectangle Area:", rectangle.Area()) } } }
Practical Examples of Polymorphism in Golang:
package main import "fmt" // Eater interface type Eater interface { Eat() string } // Dog type type Dog struct{} // Cat type type Cat struct{} // Implement Eat method for Dog func (d Dog) Eat() string { return "Dog is eating" } // Implement Eat method for Cat func (c Cat) Eat() string { return "Cat is eating" } func main() { animals := []Eater{Dog{}, Cat{}} for _, animal := range animals { fmt.Println(animal.Eat()) } }
Interface-Based Design and Polymorphism in Golang:
package main import "fmt" // Writer interface type Writer interface { Write(data string) error } // ConsoleWriter type type ConsoleWriter struct{} // FileLogger type type FileLogger struct { FilePath string } // Implement Write method for ConsoleWriter func (cw ConsoleWriter) Write(data string) error { fmt.Println("Writing to console:", data) return nil } // Implement Write method for FileLogger func (fl FileLogger) Write(data string) error { // Implement file writing logic fmt.Println("Writing to file:", data) return nil } func main() { consoleWriter := ConsoleWriter{} fileLogger := FileLogger{FilePath: "log.txt"} writeToOutput(consoleWriter, "Hello, Golang!") writeToOutput(fileLogger, "Logging data") } func writeToOutput(w Writer, data string) { w.Write(data) }
Golang Polymorphism vs Method Overloading:
package main import "fmt" // Printer interface type Printer interface { Print(data string) } // ConsolePrinter type type ConsolePrinter struct{} // Implement Print method for ConsolePrinter func (cp ConsolePrinter) Print(data string) { fmt.Println("Printing to console:", data) } func main() { consolePrinter := ConsolePrinter{} printMessage(consolePrinter, "Hello, Golang!") printMessage(consolePrinter, "Print this too!") } func printMessage(p Printer, message string) { p.Print(message) }