[Go Tutorial Series Notes] Interface Part 1

Keywords: Go Java

Catalog

What is an interface?

Interfaces in the object-oriented world are generally defined as "interfaces define the behavior of objects". It only specifies what objects should do? The way to implement this behavior (implementation details) depends on the object.

In Go, the interface is a set of method signatures. When a type defines all methods in an interface, it implements the interface. It is very similar to the OOP world. Interfaces specify the methods that a type should have, and the type determines how these methods are implemented.

For example, Washing Machine can be an interface with method signatures Cleaning() and Drying(). Any type that defines Cleaning and Drying implements Washing Machine.

<!-- more -->

Declare and implement interfaces

Let's create an interface and implement its program.

package main

import (  
    "fmt"
)

//interface definition
type VowelsFinder interface {  
    FindVowels() []rune
}

type MyString string

//MyString implements VowelsFinder
func (ms MyString) FindVowels() []rune {  
    var vowels []rune
    for _, rune := range ms {
        if rune == 'a' || rune == 'e' || rune == 'i' || rune == 'o' || rune == 'u' {
            vowels = append(vowels, rune)
        }
    }
    return vowels
}

func main() {  
    name := MyString("Sam Anderson")
    var v VowelsFinder
    v = name // possible since MyString implements VowelsFinder
    fmt.Printf("Vowels are %c", v.FindVowels())

}

An interface type called VowelsFinder is created above, which has a method signature FindVowels() []rune.

Then create a type MyString.

We add the method FindVowels () [] run to the receiver type MyString. Now MyString implements the interface VowelsFinder. This does not work with other languages such as Java, where classes must explicitly declare that interfaces are implemented using implements keywords. But this is not necessary in Go, which is implicitly implemented in Go. If the type contains all the methods declared in the interface, then it implements the interface.

Actual User Interface

We will write a simple program to calculate the company's total expenditure based on the personal wages of employees. For brevity, let's assume that all costs are in US dollars.

Suppose our company has two kinds of employees, Permanent and Contract.

package main

import (  
    "fmt"
)

type SalaryCalculator interface {  
    CalculateSalary() int
}

type Permanent struct {  
    empId    int
    basicpay int
    pf       int
}

type Contract struct {  
    empId  int
    basicpay int
}

//salary of permanent employee is sum of basic pay and pf
func (p Permanent) CalculateSalary() int {  
    return p.basicpay + p.pf
}

//salary of contract employee is the basic pay alone
func (c Contract) CalculateSalary() int {  
    return c.basicpay
}

/*
total expense is calculated by iterating though the SalaryCalculator slice and summing  
the salaries of the individual employees  
*/
func totalExpense(s []SalaryCalculator) {  
    expense := 0
    for _, v := range s {
        expense = expense + v.CalculateSalary()
    }
    fmt.Printf("Total Expense Per Month $%d", expense)
}

func main() {  
    pemp1 := Permanent{1, 5000, 20}
    pemp2 := Permanent{2, 6000, 30}
    cemp1 := Contract{3, 3000}
    employees := []SalaryCalculator{pemp1, pemp2, cemp1}
    totalExpense(employees)

}

The biggest advantage of this is that total Expense can be extended to any new employee type without changing any code.

Interface Internal Representation

It can be considered that the interface is represented by the tuple internal (type, value), type is the basic specific type of the interface, value saves the value of the specific type.

Let's write a program to better understand.

package main

import (  
    "fmt"
)

type Tester interface {  
    Test()
}

type MyFloat float64

func (m MyFloat) Test() {  
    fmt.Println(m)
}

func describe(t Tester) {  
    fmt.Printf("Interface type %T value %v\n", t, t)
}

func main() {  
    var t Tester
    f := MyFloat(89.7)
    t = f
    describe(t)
    t.Test()
}

Empty interface

Interfaces with zero methods are called empty interfaces. It is represented as interface {}. Because the method of empty interface is zero, all types of empty interface are implemented.

package main

import (  
    "fmt"
)

func describe(i interface{}) {  
    fmt.Printf("Type = %T, value = %v\n", i, i)
}

func main() {  
    s := "Hello World"
    describe(s)
    i := 55
    describe(i)
    strt := struct {
        name string
    }{
        name: "Naveen R",
    }
    describe(strt)
}

The description (i interface {}) function takes empty interfaces as parameters, so it can pass any type.

Type Asserts

Type assertions are used to extract underlying values of interfaces.

i.(T) Grammar is used to obtain the basic value of the specific type of i interface.

package main

import (  
    "fmt"
)

func assert(i interface{}) {  
    // Get the value of the underlying type from i
    s := i.(int) //get the underlying int value from i
    fmt.Println(s)
}
func main() {  
    var s interface{} = 56
    assert(s)
}

What happens if the specific type of program is not int?

If in the above program, we pass the concrete type string to the function that tries to assert to extract the int value.
The program panics and prompts errors: panic: interface conversion: interface {} is string, not int.

To solve these problems, we can use grammar:

v, ok := i.(T)  

If OK is true, the type assertion is correct, if ok is false, and v is the zero value of the specific type, and the program will not panic.

Type Switch

Type switch is used to compare the specific type of interface with the type specified in each case statement. Similar to switch case, the only difference is that the type switch specifies the type rather than the value in the switch case.

switch i.(type) {
case string:
    fmt.Printf("I am a string and my value is %s\n", i.(string))
case int:
    fmt.Printf("I am an int and my value is %d\n", i.(int))
default:
    fmt.Printf("Unknown type\n")
}

You can also compare types and interfaces. If we have a type that implements an interface, we can compare that type with the interface it implements.

Let's write a program to make it clearer.

package main

import "fmt"

type Describer interface {  
    Describe()
}
type Person struct {  
    name string
    age  int
}

func (p Person) Describe() {  
    fmt.Printf("%s is %d years old", p.name, p.age)
}

func findType(i interface{}) {  
    switch v := i.(type) {
    case Describer:
        v.Describe()
    default:
        fmt.Printf("unknown type\n")
    }
}

func main() {  
    findType("Naveen")
    p := Person{
        name: "Naveen R",
        age:  25,
    }
    findType(p)
}

Resources

https://golangbot.com/interfa...

Posted by SuNcO on Wed, 14 Aug 2019 01:15:21 -0700