Polymorphism in Go

Polymorphism in Go

In this tutorial, we will discuss polymorphism and how we can achieve polymorphism in the Go language. We can achieve Polymorphism in the Go language with the help of interfaces. As we have already discussed, we can implicitly interface in the Go language.

Polymorphism in Go

A type implements an interface if it provides definitions for all the methods declared in the interface. Let’s see how polymorphism is achieved in Go language with the help of interfaces.

Polymorphism using interface

In Go language, any type that defines all the interface methods is said to implement that interface implicitly.

A variable of type interface can hold any value which implements the interface. This property of interfaces is used to achieve polymorphism in the Go language.

Let’s stop the theory and write some program that calculates an organisation’s net income right away ?.

For simplicity, let’s assume that this imaginary organization has income from two kinds of projects, viz. fixed billing and time and material. The net income of the organization is calculated by the sum of the incomes from these projects.

To keep this tutorial simple, we will assume that the currency is INR (Indian Rupee), and we will not deal with dollars or cents. It will be represented using int data type.

Example Code Snippets

Now let’s first define an interface Income.

type Income interface {  
    calculate() int
    source() string
}

In the above code, the Income interface defined above contains two methods calculate(), which calculates and returns the income from the source and source(), which returns the source’s name.

Next, let’s define a struct for the FixedBilling project type.

type FixedBilling struct {  
    projectName string
    biddedAmount int
}

In the above code, the FixedBilling project has two fields projectName, which represents the project’s name and biddedAmount, which is the amount the organisation has bid for the project.

Now the TimeAndMaterial struct will represent projects of Time and Material type.

type TimeAndMaterial struct {  
    projectName string
    noOfHours  int
    hourlyRate int
}

In the above code, The TimeAndMaterial struct has three fields names projectName, noOfHours and hourlyRate.

The next step would be to define methods on these struct types, which calculate and return the actual income and source of income.

func (fb FixedBilling) calculate() int {  
    return fb.biddedAmount
}

func (fb FixedBilling) source() string {  
    return fb.projectName
}

func (tm TimeAndMaterial) calculate() int {  
    return tm.noOfHours * tm.hourlyRate
}

func (tm TimeAndMaterial) source() string {  
    return tm.projectName
}

In the case of FixedBilling projects, the income is just the amount bid for the project. Hence we return this from the calculate() method of FixedBilling type.

In the case of TimeAndMaterial projects, the income is the product of the noOfHours and hourlyRate. This value is returned from the calculate() method with receiver type TimeAndMaterial.

We return the name of the project as a source of income from the source() method.

Since both FixedBilling and TimeAndMaterial structs provide definitions for the calculate() and source() methods of the Income interface, both structs implement the Income interface.

Now let’s add the calculateNetIncome function, which will calculate and print the total income.

func calculateNetIncome(ic []Income) {  
    var netincome int = 0
    for _, income := range ic {
        fmt.Printf("Income From %s = ₹%d\n", income.source(), income.calculate())
        netincome += income.calculate()
    }
    fmt.Printf("Net income of organization = ₹%d", netincome)
}

The calculateNetIncome function above accepts a slice of Income interface as an argument.

The above method calculates the total income by iterating over the slice and calling the calculate() method on each item. This method also displays the income source by calling source() method.

Depending on the concrete type of the Income interface, different calculate() and source() methods will be called. Thus we have achieved polymorphism in the calculateNetIncome function.

In the future, if the organization adds a new kind of income source, this function will still calculate the total income correctly without a single line of code change :).

Main Function

Now The only part remaining in the program is the main function.

func main() {  
    project1 := FixedBilling{projectName: "Project 1", biddedAmount: 150000}
    project2 := FixedBilling{projectName: "Project 2", biddedAmount: 250000}
    project3 := TimeAndMaterial{projectName: "Project 3", noOfHours: 160, hourlyRate: 250}
    incomeStreams := []Income{project1, project2, project3}
    calculateNetIncome(incomeStreams)
}

In the main function above, we have created three projects, two of type FixedBilling and one of type TimeAndMaterial. Next, we create a slice of type Income with these 3 projects.

Since each of these projects has implemented the Income interface, it is possible to add all three projects to a slice of type Income.

So finally, we call calculateNetIncome function with this slice, and it will display the various income sources and the income from them.

package main

import (
    "fmt"
)

type Income interface {
    calculate() int
    source() string
}

type FixedBilling struct {
    projectName  string
    biddedAmount int
}

type TimeAndMaterial struct {
    projectName string
    noOfHours   int
    hourlyRate  int
}

func (fb FixedBilling) calculate() int {
    return fb.biddedAmount
}

func (fb FixedBilling) source() string {
     return fb.projectName
 }
 func (tm TimeAndMaterial) calculate() int {
     return tm.noOfHours * tm.hourlyRate
 }
 func (tm TimeAndMaterial) source() string {
     return tm.projectName
 }
 func calculateNetIncome(ic []Income) {
     var netincome int = 0
     for _, income := range ic {
         fmt.Printf("Income From %s = ₹%d\n", income.source(), income.calculate())
         netincome += income.calculate()
     }
     fmt.Printf("Net income of organization = ₹%d", netincome)
 }
 func main() {
     project1 := FixedBilling{projectName: "Project 1", biddedAmount: 150000}
     project2 := FixedBilling{projectName: "Project 2", biddedAmount: 250000}
     project3 := TimeAndMaterial{projectName: "Project 3", noOfHours: 160, hourlyRate: 250}
     incomeStreams := []Income{project1, project2, project3}
     calculateNetIncome(incomeStreams)
 }

Output

Income From Project 1 = ₹150000 
Income From Project 2 = ₹250000 
Income From Project 3 = ₹40000 
Net income of organization = ₹440000

Run in playground

Polymorphism in Go
Scroll to top