Table of contents
Polymorphism is the ability of an object or function to take on multiple forms. In the Go programming language, polymorphism is supported through the use of interfaces.
An interface in Go defines a set of methods that a type must implement to satisfy the interface. This means that any type that implements the methods defined in an interface is said to implement the interface.
For example, suppose we have an interface called Shape
that defines the methods Area()
and Perimeter()
for calculating the area and perimeter of a shape. We could define the Shape
interface like this:
type Shape interface {
Area() float64
Perimeter() float64
}
Now, any type that implements the Area()
and Perimeter()
methods can be said to implement the Shape
interface. For example, we could define a Rectangle
type that satisfies the Shape
interface like this:
type Rectangle struct {
width float64
height float64
}
func (r *Rectangle) Area() float64 {
return r.width * r.height
}
func (r *Rectangle) Perimeter() float64 {
return 2 * (r.width + r.height)
}
Here, the Rectangle
type implements the Area()
and Perimeter()
methods, so it satisfies the Shape
interface. This means that we can use a Rectangle
value wherever a Shape
is expected, and the appropriate method will be called automatically.
In this way, polymorphism in Go allows us to write code that can work with multiple types in a consistent manner, without needing to know the specific type of an object at compile time.
Example Code
package main
import "fmt"
type person struct{
firstname string;
lastname string
}
type human interface{
talk()
}
func (p person) talk(){
fmt.Println("My name is", p.firstname)
}
func main(){
person1 := person{
firstname: "Alfred",
lastname: "Johnson-awah",
}
person1.talk()
}
In the code above we created a person struct
which has the firstname
and lastname
properties. To showcase polymorphism, we created an interface with a function of talk. Lastly, we have implemented the talk function and attached it to a struct type of person
. This means that any identifier of type person
would always have a method talk
and at the end of the day, we can boldly say that the identifier (person1
in our case) is of both person
and human
type, this is polymorphism.
You can still go ahead and add more complex code by implementing type assertion using a switch
statement as seen below.
package main
import "fmt"
type person struct {
firstname string
lastname string
}
type seniorMan struct {
person
hasACar bool
}
type human interface {
talk()
}
func (p person) talk() {
fmt.Println("My name is", p.firstname)
}
func (p seniorMan) talk() {
fmt.Println("I am a person and senior man, see my ID", p, "and i have a car too", p.hasACar)
}
func resolveType(h human) {
switch t := h.(type) {
case person:
fmt.Println("This is a normal person", t.firstname)
case seniorMan:
fmt.Println("This is a senior man, does this person have a car?", t.hasACar)
}
}
func main() {
person1 := person{
firstname: "Alfred",
lastname: "Johnson-awah",
}
seniorMan1 := seniorMan{
person: person{
firstname: "Joe",
lastname: "Biden",
},
hasACar: true,
}
resolveType(seniorMan1)
resolveType(person1)
person1.talk()
}
Running this code on the console, your output should be like the below:
This is a senior man, does this person have a car? true
This is a normal person Alfred
My name is Alfred
The seniorMan
struct type inherits properties of the person
struct type with an additional property hasACar
. The most important exerpt of this code is the resolveType
function which uses a switch state to person type assertion on interfaces to find out their underlying types and execute the correct block of code.
Conclusion
In this blog post, we learnt about polymorphism and how it stems from interface implementation.