GoLang OOP

📢 This article was translated by gemini-3-flash-preview

Golang Series

Hello GoLang: https://blog.yexca.net/archives/154
GoLang (var and const) Variables and Constants: https://blog.yexca.net/archives/155
GoLang (func) Functions: https://blog.yexca.net/archives/156
GoLang (slice and map) Slices: https://blog.yexca.net/archives/160
GoLang (OOP) Object-Oriented Programming: This Article
GoLang (reflect) Reflection: https://blog.yexca.net/archives/204
GoLang (struct tag) Struct Tags: https://blog.yexca.net/archives/205
GoLang (goroutine) Goroutines: https://blog.yexca.net/archives/206
GoLang (channel) Channels: https://blog.yexca.net/archives/207


Building concepts of classes and objects using structs.

Learn about OOP basics: OOP Fundamentals

struct

First, custom types. Use the type keyword, similar to C.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import "fmt"

type myType int

func main() {
    var a myType
    fmt.Println("a =", a)
    fmt.Printf("type of a is %T", a)
}

/*
 * Output
 * a = 0
 * type of a is main.myType
 */

Defining types using struct:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import "fmt"

type Person struct {
    name string
    age  int
}

func main() {
    var zhang Person
    zhang.name = "zhangSan"
    zhang.age = 20

    fmt.Print(zhang)
}

Regarding function parameter passing: it’s pass-by-value if pointers aren’t used.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import "fmt"

type Person struct {
    name string
    age  int
}

// Pass by value, won't modify original data
func changeName(person Person) {
    person.name = "liSi"
}

// Pass by reference, will modify original data
func changeAge(person *Person) {
    person.age = 18
}

func main() {
    var zhang Person
    zhang.name = "zhangSan"
    zhang.age = 20

    fmt.Println(zhang)

    changeName(zhang)
    fmt.Println(zhang)

    changeAge(&zhang)
    fmt.Println(zhang)
}

Encapsulation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import "fmt"

// Class
type Person struct {
    // Properties
    name string
    age  int
}

// Methods
func (this *Person) SetName(name string) {
    this.name = name
}
func (this *Person) SetAge(age int) {
    this.age = age
}
func (this *Person) GetName() string {
    return this.name
}
func (this *Person) GetAge() int {
    return this.age
}

func main() {
    person := Person{name: "zhangSan", age: 18}
    fmt.Println(person)
}

Note that if the first letter of the Class name or Method name is capitalized, it can be accessed by other packages (like public in Java). If the first letter is lowercase, it is package-private (only accessible within the same package). The same applies to property names.

Inheritance

The following code resides in the same file. Parent class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Parent class
type Person struct {
    name string
    age  int
}

// Parent methods
func (this *Person) Eat() {
    fmt.Println("Person Eat...")
}
func (this *Person) Walk() {
    fmt.Println("Person Walk...")
}

Child class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Child class
type SuperMan struct {
    Person     // Inherit parent class
    level  int // Child property
}

// Child overrides parent method
func (this *SuperMan) Walk() {
    fmt.Println("SuperMan Walk")
}
// Child specific method
func (this *SuperMan) Fly() {}
    

main function:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func main() {
    // Define child properties, method 1
    superMan1 := SuperMan{Person{"zhangSan", 18}, 4}
    // Define child properties, method 2
    var superMan2 SuperMan
    superMan2.name = "liSi" // Accessible since it's the same package
    superMan2.age = 20
    superMan2.level = 5

    // Child calling parent method
    superMan1.Eat()
    // Child overridden method
    superMan1.Walk()
    // Child method
    superMan2.Fly()
}

Polymorphism

Defining an interface in the same file:

1
2
3
4
5
6
// Interface, essentially a pointer
type Animal interface {
    Sleep()
    GetName() string
    GetType() string
}

Implementation one:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// Implementation one
type Cat struct {
    name string
    kind string
}

// Implement all interface methods
func (this *Cat) Sleep() {
    fmt.Println("Cat Sleep()")
}
func (this *Cat) GetName() string {
    return this.name
}
func (this *Cat) GetType() string {
    return this.kind
}

Implementation two:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Implementation two
type Dog struct {
    name string
    kind string
}

// Implement all interface methods
func (this *Dog) Sleep() {
    fmt.Println("Dog Sleep()")
}
func (this *Dog) GetName() string {
    return this.name
}
func (this *Dog) GetType() string {
    return this.kind
}

func ShowAnimal(animal Animal) {
    fmt.Println(animal)
}

main:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
func ShowAnimal(animal Animal) {
    fmt.Println(animal)
}

func main() {
    var animal Animal
    animal = &Cat{"cat1", "cat"}
    animal.Sleep()
    animal = &Dog{"dog1", "dog"}
    animal.Sleep()

    cat := Cat{"cat2", "cat"}
    dog := Dog{"dog2", "dog"}

    ShowAnimal(&cat)
    ShowAnimal(&dog)

}

Universal Type and Assertions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import "fmt"

// Use empty interface as a universal data type
func f(arg interface{}) {
    // Type assertion mechanism (type casting)
    value, flag := arg.(string)
    if !flag {
        fmt.Println("arg is not string type")
    } else {
        fmt.Println("arg is string type, arg =", value)
    }
}

func main() {
    // Can pass any data type, including custom ones
    f("abc")
    f(123)
    f(3.14)
}

/*
 * Output
 * arg is string type, arg = abc
 * arg is not string type
 * arg is not string type
 */