Contents

Methods and Interfaces

Syntax

In Go, methods are functions with a special receiver argument, which allows you to define functions on types (including structs). A method is defined by specifying the receiver (which can be a struct or any other type) before the method name. Methods allow you to associate behaviors with specific types, similar to how classes and methods work in object-oriented programming. Methods can be used to manipulate the data within a struct or perform operations related to that struct.

Example:

				
					type Rectangle struct {
    Width, Height int
}

// Method to calculate the area of the rectangle
func (r Rectangle) Area() int {
    return r.Width * r.Height
}

rect := Rectangle{Width: 10, Height: 5}
fmt.Println(rect.Area())  // Output: 50

				
			

Pointers and Methods

Methods can be associated with both value receivers and pointer receivers. When a method has a pointer receiver, it can modify the value that the receiver points to. This is useful for avoiding unnecessary copying of large structs and for updating the state of the receiver. When calling a method with a pointer receiver, Go automatically handles the conversion between values and pointers, so you can call the method on both the value and the pointer of the type.

Example:

				
					type Counter struct {
    Count int
}

// Method with a pointer receiver to increment the counter
func (c *Counter) Increment() {
    c.Count++
}

counter := Counter{Count: 0}

// Calling the method using a value
counter.Increment()
fmt.Println(counter.Count)  // Output: 1

// Calling the method using a pointer
counterPtr := &counter
counterPtr.Increment()
fmt.Println(counter.Count)  // Output: 2

				
			

Interfaces

Interfaces in Go are abstract types that specify a set of method signatures but do not implement them. Any type that implements these methods is considered to satisfy the interface. Interfaces are a key feature in Go’s type system, enabling polymorphism and allowing different types to be used interchangeably as long as they implement the required methods. Interfaces are often used to define behaviors or capabilities that multiple types can share.

Example:

				
					type Shape interface {
    Area() float64
}

type Circle struct {
    Radius float64
}

// Implementing the Shape interface for Circle
func (c Circle) Area() float64 {
    return 3.14 * c.Radius * c.Radius
}

type Rectangle struct {
    Width, Height float64
}

// Implementing the Shape interface for Rectangle
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// Function that takes a Shape interface
func printArea(s Shape) {
    fmt.Println(s.Area())
}

circle := Circle{Radius: 5}
rectangle := Rectangle{Width: 10, Height: 5}

printArea(circle)     // Output: 78.5
printArea(rectangle)  // Output: 50

				
			

Type Assertions and Type Switches

Type assertions and type switches are used in Go to work with interfaces and retrieve the dynamic type of an interface value. Type assertions allow you to extract the underlying value from an interface if you know its concrete type. Type switches are similar to switch statements but are used to handle multiple types within an interface. They are particularly useful when working with interfaces that may hold values of different types.

Examples

				
					package main

import "fmt"

func main() {
    // Explicit type declaration
    var name string = "John"
    var age int = 25

    // Type inference with var
    var height = 175.5

    // Shorthand declaration
    country := "USA"

    fmt.Println(name, age, height, country)
}

				
			

Type Switch:

				
					func doSomething(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Println("Integer:", v)
    case string:
        fmt.Println("String:", v)
    default:
        fmt.Println("Unknown type")
    }
}

doSomething(10)       // Output: Integer: 10
doSomething("hello")  // Output: String: hello
doSomething(3.14)     // Output: Unknown type