Go Basic: Understanding Methods, Interfaces, Struct Embedding, Generics: Covering OOP concepts in Go

Let’s dive into the exciting world of Object-Oriented Programming (OOP) in Go. 😄

full lessons here👇:
https://programmerscareer.com/golang-basic-skill/

Generated by AI, there may be errors, for reference only

Topic: Introduction to Object-Oriented Programming in Go

Historically, Go has been represented as a language that does not support traditional object-oriented programming semantics. This, however, is not entirely accurate. In Go, we can’t create classes in the traditional sense used in languages like Java or Python, but we can define methods on types. This is Go’s way of letting us define behaviour for our own types, and this is comparable with the concept of a ‘class’ in other languages.

In essence, Go is more about composing functionality and providing only the simplest of mechanisms to do so. This simplicity results in readable and maintainable code that serves as a major advantage of Go over other object-oriented programming languages.

Remember, the aim of Object-Oriented Programming (OOP) is to design the program using data and functions as a single unit called an object. Similarly, with Go’s approach, we can also encapsulate data and functions into a single entity.

Topic: Structures in Go

Structures, or structs as we call them commonly in Go, are custom types that group together zero or more other types.

We define a new struct type using the type keyword, followed by the name of the struct, and then the keyword struct. The actual definition of the struct fields is contained within braces {}.

Here’s a basic example:

1
2
3
4
type Person struct {  
Name string
Age int
}

In this example, we’re defining a new type named Person with two fields: Name, which is a string, and Age which is an integer.

We can then create instances of our Person struct like so:

1
2
3
p := Person{"John Doe", 30}  
fmt.Println(p.Name) // Output: John Doe
fmt.Println(p.Age) // Output: 30

In Go, the . operator is used to access the fields of a struct instance. As you can see structs are very powerful for grouping related data together. It’s essentially Go’s approach to building classes like you would in other languages.

Topic: Methods in Go

In Go, a method is a function that is associated with a particular type. Methods on types are what provide the ‘object-oriented’ feel to the Go programming language, even though strictly speaking, Go does not have objects or classes. Here’s a simple demonstration how to declare a method in Go:

1
2
3
4
5
6
type Circle struct {  
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}

In this example, we define a new type Circle and then we define a method Area() that operates on the type Circle.

1
2
c := Circle{10}  
fmt.Println(c.Area()) // Outputs: 314.1592653589793

This above example is interesting because now we’re calling a function directly on an instance of Circle - this is similar behavior to calling methods on objects in languages like Java or Javascript.

let’s discuss pointer receivers in Go methods.

Topic: Pointer Receivers in Go

In Go, you can also declare methods with pointer receivers. This means the receiver type has the * operator before it. Here’s an example:

1
2
3
4
5
6
7
type Employee struct {  
Name string
Salary int
}
func (e *Employee) giveRaise(amount int) {
e.Salary += amount
}

In this example, giveRaise() is a method with a pointer receiver of type Employee. Now, when we call giveRaise(), it will modify the original Employee variable, because it has a reference to the original item.

1
2
3
e := Employee{"John", 5000}  
e.giveRaise(1000)
fmt.Println(e.Salary) // Outputs: 6000

You may ask, why and when to use pointer receivers?

  1. If we need to modify the receiver, we should use a pointer receiver. Like in the giveRaise() example.
  2. If the struct is large, a pointer receiver will be more efficient, because it doesn’t need to copy the entire struct.

Topic: Interfaces in Go

An interface in Go is a type that simply defines a set of methods. If any other type implements these methods, then that type is considered to implement the interface. Here’s an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type Shape interface {  
Area() float64
}

type Circle struct {
Radius float64
}

func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}

type Rectangle struct {
Width, Height float64
}

func (r Rectangle) Area() float64 {
return r.Width * r.Height
}

In this code, we define an interface Shape that has a single method Area(). Then we define two different structs, Circle and Rectangle, each with an Area() method. Both Circle and Rectangle now implement the Shapeinterface. We can treat instances of Circle and Rectangle as Shape:

1
2
3
4
5
6
7
func printArea(s Shape) {  
fmt.Println(s.Area())
}
c := Circle{10}
r := Rectangle{10, 20}
printArea(c) // Outputs: 314.1592653589793
printArea(r) // Outputs: 200

Go interface is different than that in other languages. In Go, we don’t need to explicitly declare that a type implements an interface. If a type provides all methods required by an interface, it automatically implements that interface.

Topic: Struct Embedding and Composition in Go

In Go, we can include or ‘embed’ one struct inside another. This ‘embedding’ provides a lot of the benefits of inheritance, but it’s much simpler and safer. In terms of object-oriented concepts, it’s more like composition (combining simple objects to form more complex ones) rather than inheritance. Here’s an example:

1
2
3
4
5
6
7
8
9
type Person struct {  
Name string
Age int

type Employee struct {
Person
Position string
Salary int
}

In the above example, Employee struct embeds Person struct, meaning it includes all the fields and methods of the Person.

We can access the fields of the Person struct directly from Employee. In fact, it’s as if these fields exist directly on the Employee struct:

1
2
3
4
5
6
7
8
9
10
11
e := Employee{  
Person: Person{
Name: "Bob",
Age: 30,
},
Position: "Developer",
Salary: 80000,
}
fmt.Println(e.Name) // Outputs: Bob
fmt.Println(e.Age) // Outputs: 30
fmt.Println(e.Salary) // Outputs: 80000

Topic: Generics in Go

Generics, which is to be introduced in Go 1.18, is one of the most awaited features in Go. Generics can make your programs more flexible and maintainable by representing a range of types, as opposed to a single one. Here is a simple example of how generics might look:

1
2
3
4
5
6
7
8
9
func PrintSlice[T any](s []T) {  
for _, v := range s {
fmt.Println(v)
}
}
func main() {
PrintSlice[int]([]int{1, 2, 3, 4, 5})
PrintSlice[string]([]string{"Hello", "World"})
}

In this code, PrintSlice[T any](s []T) is a generic function that works for a slice of any type. T is a type parameterit stands for any type. The anykeyword means that T could be any type at all: an int, float, string, etc., including user-defined types!

In the main function, we call PrintSlice with slices of int and string. This demonstrates that PrintSlice is a generic function.

this example gives you a basic understanding of how generics would work in Go.

中文文章: https://programmerscareer.com/zh-cn/go-basic-04/
Author: Wesley Wei – Twitter Wesley Wei – Medium
Note: If you choose to repost or use this article, please cite the original source.

Go Basic: Understanding Error Handling: Errors, Panic, Defer, Recover in Go Go Basic: Understanding For, If Else, Switch, Functions, Range, Closures, Recursion in Go

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×