Cyberithub

Understanding Golang Interfaces [Explained with best examples]

Advertisements

In this tutorial, we will understand Golang interfaces with the help of some real world examples. Go does not directly support Object Oriented Programming (OOP) because it does not contain the concept of classes. But methods and interfaces can be used to implement object oriented code in Go. This article assumes that you understand methods, therefore we will explore interfaces and how to implement interfaces in Go.

 

Prerequisites

 In order to follow through this tutorial:-

Advertisements
  • You need to have a basic understanding of programming.
  • You need to download the Go compiler from the official website and follow the directions to install it based on your operating system being either Windows, MacOS or any of the Linux distributions.
  • You need a code editor like visual studio, atom or sublime text. Alternatively, you can download an Integrated Development Environment (IDE). An IDE is an improved code editor with added qualities customized to help you write and execute codes easier. Goland is the most popular IDE used for developing Go programs.
  • You need to have an understanding of Go data types, structs, functions and methods.

 

Understanding Golang Interfaces [Explained with best examples]

Understanding Golang Interfaces [Explained with best examples]

Also Read: Introduction to Type Casting in Golang [Explained with examples]

An interface is an abstract data type in Go made up of method signatures. Interfaces are used to hide the direct properties of a type, but instead defines the type behaviorally (what it can do). For example, we can define a Vehicle interface with methods Drive, Stop and Reverse. An interface is defined using the type keyword, the name of the interface and the interface keyword with a set of method signatures within braces.

Advertisements

Understanding Golang Interfaces [Explained with best examples] 2

The interface above suggests that any type that can drive, stop and reverse can be treated as a vehicle. If we create a type, and define the Drive, Stop and Reverse methods for it, we can say that the type implements the interface Vehicle. Let's write a program to implement the Vehicle interface.

Understanding Golang Interfaces [Explained with best examples] 3

In the snippet above, Car is a new type defined with its underlying type as a string. We attached the Drive, Reverse and Stop methods to Car to implement the interface Vehicle. A value of type Car is now an instance of Vehicle. Let’s demonstrate that in the main function.

Advertisements

Understanding Golang Interfaces [Explained with best examples] 4

In the main function above, we first define a variable v of type Vehicle, then define a variable of type Car. On line 30,  we were able to assign audi, of type Car to v because Car implements the Vehicle interface. Let’s put the example together.

Understanding Golang Interfaces [Explained with best examples] 5

Output

Advertisements

Understanding Golang Interfaces [Explained with best examples] 6

In Go - unlike a language like Java - a type implicitly implements an interface if it defines methods described in the interface. An interface variable can accept values of any type as far as the type implements the interface.

An interface is used when you have two or more types that behave the same way. The example above is only trying to portray how a type can implement an interface, and its effect. Let's modify that example to a practical example where interfaces are needed.

Understanding Golang Interfaces [Explained with best examples] 7

Understanding Golang Interfaces [Explained with best examples] 8

Understanding Golang Interfaces [Explained with best examples] 9

Output

Understanding Golang Interfaces [Explained with best examples] 10

The program above notifies a user that their vehicles have stopped and are refueling. From line 14 and 28, we define two types Car and Bus, then define methods to implement the Vehicle Interface. On line 42, we define a refuelVehicle function that takes in a Vehicle and informs the user that the vehicle has stopped, and is refueling. Since Car and Bus both behave the same way, and implement the Vehicle interface, we were able to add variables of both types to a slice and refuel them in order.

Let’s examine a very popular example of using an interface in the Go standard library. The package io defines an interface Writer which contains a single method.

Understanding Golang Interfaces [Explained with best examples] 11

Writer defines any type that can be written to, such as a file, network, buffers, http clients and so on. Any type which has the Write() method as shown in the program above implements the Writer interface.

From the fmt package, Fprintf accepts an io.Writer and a value to write to the Writer. Fprintf in turn, calls the Write method on Writer with the value passed in as its parameter. Let’s see the implementation of Fprintf from the standard library.

Understanding Golang Interfaces [Explained with best examples] 12

From the snippet above, we see that Fprintf just calls the Write method of Writer on line 205. This means that Fprintf can accept any value of a type that implements the Writer interface (has the Write method). From the fmt package, Printf takes advantage of this quality to write a value to a variable of type  *os.FIle named os.Stdout. os.Stdout is the default output stream of your computer (/dev/stdout on a linux machine). Let’s see the definitions of  fmt.Printf.

Understanding Golang Interfaces [Explained with best examples] 13

With this understanding, we can define a new type that implements the Writer interface, and pass it into fmt.Fprintf. Let's demonstrate this below.

Understanding Golang Interfaces [Explained with best examples] 14

Output

Understanding Golang Interfaces [Explained with best examples] 15

Notice the naming convention for an interface with a single method. The interface is named by adding -er after the method name. Other examples that use this convention from the standard library are Closer and Reader containing the Close and Read methods respectively.

 

Interface Values

An interface value is basically made up of a tuple: the dynamic type and dynamic value. The dynamic type is the type of the dynamic value stored in the interface value. In the example program immediately above, the dynamic type of io.Writer passed into fmt.Fprintf is NewString, while its dynamic type is the variable val.  Type assertions as we’ll see below converts an interface value to its dynamic type.

 

Nil Interface

A nil interface has no dynamic type and dynamic value. It is the default value of an interface. Do not confuse a nil interface with an interface with a nil dynamic value. As long as an interface value has a dynamic type, it is not a nil interface.

Understanding Golang Interfaces [Explained with best examples] 16

Output

Understanding Golang Interfaces [Explained with best examples] 17

 

Empty Interface

An empty interface is an interface defined with no methods. Every type in Go implements at least no methods, therefore any type can be stored in an empty interface. Empty interfaces are typically used to be able to receive all types of values into a function or method. Functions like Printf and Sprintf use the empty interface to accept any value. The empty interface is defined in the form interface{}.

Understanding Golang Interfaces [Explained with best examples] 18

Output

Understanding Golang Interfaces [Explained with best examples] 19

 

Type Assertion

As explained above, an empty interface can accept values of any type. But take note, the value stored will not be treated like its initial type, instead it will be treated like an empty interface with no allowed default operation. We can get the initial type of the value back using a Type Assertion.

A type assertion is implemented using the expression value.(T), where value is a variable of type interface{} and T represents the datatype to convert value to. If T is not the dynamic type of value, the compiler panics. Let's write a quick example to describe this behavior.

Understanding Golang Interfaces [Explained with best examples] 20

Output

Understanding Golang Interfaces [Explained with best examples] 21

A type assertion can be used to assert any interface to its dynamic type, but in most cases, it is used with empty interfaces.

 

Type Switches

Type switches are like switch statements, except that each case specifies types and not values. A type switch is used to compare the dynamic type of an empty interface against multiple types. Let’s see how this works below.

Understanding Golang Interfaces [Explained with best examples] 22

Output

Understanding Golang Interfaces [Explained with best examples] 23

The type switch works by converting the empty interface value into each data type at each case until it finds the one that satisfies the interfaces’ dynamic type. Type switches are usually applied within functions that accept any value and execute actions based on the type of the value received.

 

Implementing Multiple Interfaces

A type can implement multiple interfaces if it possesses the methods from multiple interfaces. This means that a value of the type can be used as an instance of any of the interfaces. For example, *os.File implements the Reader, Writer and Closer interfaces since it possesses the Read, Write and Close methods respectively.

 

Embedding Interfaces

From the builtin io package, there’s an interface called ReadWriter, this is a combination of the Reader and Writer interfaces. ReadWriter is defined like:-

Understanding Golang Interfaces [Explained with best examples] 24

This combination in the program above is called interface embedding. Therefore, any type that implements both the Reader and Writer interfaces automatically implements ReadWriter. We can instead define ReadWriter by writing the methods of Reader and Writer in ReadWriter with the same behavior, but that would be less concise.

 

Interface Receivers

In the examples above, we have implemented interfaces using both pointer and value receivers. Unlike methods, if type *T automatically implements an interface, a value of type T does not implement the same interface, where T represents a data type. Therefore, a value of type T cannot be stored in the interface.

The first example we used in this article uses a value receiver to implement the interface Vehicle. Let’s modify that example to use a pointer receiver instead.

Understanding Golang Interfaces [Explained with best examples] 25

 

Polymorphism

Polymorphism is an essential concept in Object Oriented programming. Polymorphism basically means having many forms or being able to behave like many forms. An interface is automatically implemented by a type that defines its methods. An interface type can accept any type that implements the interface. This property allows interfaces to achieve polymorphism in Go.

Let’s see an example to explain how this works. We will write a program that helps a landlord calculate the total amount of rent due at a particular time.

Understanding Golang Interfaces [Explained with best examples] 26

Understanding Golang Interfaces [Explained with best examples] 27

Understanding Golang Interfaces [Explained with best examples] 28

In the program above, from lines 5 - 9, we define an interface Property containing the methods calculateRent(), age() and location(). We then define two structs Flat and Hostel and define methods on them to implement the Property interface. Between lines 50 - 56, we define the totalRentOwed() function to accept a slice of Property and return the total rent owed from each property.

In the main function, we defined variables of type Flat and Hostel, and added them to a slice named properties. We were able to pass properties into totalRentOwed() since each slice element implements the Property interface. If we run the program, we get the result below.

Understanding Golang Interfaces [Explained with best examples] 29

We were able to polymorphism in the totalRentOwed() method since it accepts values of different forms and implements them in the same way. If the landlord from the program above decides to purchase and rent out a duplex, we can easily create a type for it and implement the Property interface. Polymorphism then allows us to calculate the total rent, including the rent of the duplex, with totalRentOwed().

 

Conclusion

Interfaces are a very powerful tool in Golang for writing decoupled and maintainable programs. Some programmers start writing programs by declaring interfaces, then defining types to implement them. This can most times lead to writing unnecessary interfaces where methods alone may be more efficient. Interfaces should be used only when you notice your program has types with similar behaviors.

Leave a Comment